summaryrefslogtreecommitdiff
path: root/sapi
diff options
context:
space:
mode:
Diffstat (limited to 'sapi')
-rw-r--r--sapi/apache2handler/php_apache.h6
-rw-r--r--sapi/apache2handler/sapi_apache2.c44
-rw-r--r--sapi/cgi/cgi_main.c192
-rw-r--r--sapi/cli/config.w326
-rw-r--r--sapi/cli/php_cli.c83
-rw-r--r--sapi/cli/php_cli_server.c51
-rw-r--r--sapi/cli/php_http_parser.c2
-rw-r--r--sapi/cli/ps_title.c40
-rw-r--r--sapi/cli/tests/017.phpt3
-rw-r--r--sapi/cli/tests/argv_mb.phpt38
-rw-r--r--sapi/cli/tests/bug43177.phpt4
-rw-r--r--sapi/cli/tests/bug65066_100.phpt1
-rw-r--r--sapi/cli/tests/bug65066_422.phpt1
-rw-r--r--sapi/cli/tests/bug65066_511.phpt1
-rw-r--r--sapi/cli/tests/bug65633.phpt1
-rw-r--r--sapi/cli/tests/bug66606_2.phpt1
-rw-r--r--sapi/cli/tests/bug66830.phpt1
-rw-r--r--sapi/cli/tests/bug67429.phpt2
-rw-r--r--sapi/cli/tests/bug68745.phpt1
-rw-r--r--sapi/cli/tests/bug71005.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_004.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_005.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_006.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_007.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_008.phpt2
-rw-r--r--sapi/cli/tests/php_cli_server_009.phpt2
-rw-r--r--sapi/cli/tests/php_cli_server_010.phpt2
-rw-r--r--sapi/cli/tests/php_cli_server_012.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_013.phpt3
-rw-r--r--sapi/cli/tests/php_cli_server_014.phpt2
-rw-r--r--sapi/cli/tests/php_cli_server_015.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_017.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_018.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_019.phpt1
-rw-r--r--sapi/cli/tests/php_cli_server_020.phpt1
-rw-r--r--sapi/cli/tests/upload_2G.phpt1
-rw-r--r--sapi/embed/php_embed.c4
-rw-r--r--sapi/fpm/fpm/fpm_conf.c10
-rw-r--r--sapi/fpm/fpm/fpm_events.c3
-rw-r--r--sapi/fpm/fpm/fpm_log.c4
-rw-r--r--sapi/fpm/fpm/fpm_main.c11
-rw-r--r--sapi/fpm/fpm/fpm_php_trace.c4
-rw-r--r--sapi/fpm/fpm/fpm_scoreboard.c12
-rw-r--r--sapi/fpm/fpm/fpm_signals.c2
-rw-r--r--sapi/fpm/fpm/fpm_status.c3
-rw-r--r--sapi/fpm/php-fpm.conf.in12
-rw-r--r--sapi/fpm/php-fpm.service.in5
-rw-r--r--sapi/litespeed/lsapi_main.c22
-rw-r--r--sapi/litespeed/lsapilib.c4
-rw-r--r--sapi/phpdbg/phpdbg.c362
-rw-r--r--sapi/phpdbg/phpdbg.h16
-rw-r--r--sapi/phpdbg/phpdbg_btree.c35
-rw-r--r--sapi/phpdbg/phpdbg_btree.h2
-rw-r--r--sapi/phpdbg/phpdbg_cmd.h2
-rw-r--r--sapi/phpdbg/phpdbg_frame.c125
-rw-r--r--sapi/phpdbg/phpdbg_frame.h2
-rw-r--r--sapi/phpdbg/phpdbg_help.c75
-rw-r--r--sapi/phpdbg/phpdbg_list.c18
-rw-r--r--sapi/phpdbg/phpdbg_list.h1
-rw-r--r--sapi/phpdbg/phpdbg_opcode.c115
-rw-r--r--sapi/phpdbg/phpdbg_print.c6
-rw-r--r--sapi/phpdbg/phpdbg_prompt.c127
-rw-r--r--sapi/phpdbg/phpdbg_prompt.h1
-rw-r--r--sapi/phpdbg/phpdbg_utils.c16
-rw-r--r--sapi/phpdbg/phpdbg_watch.c1907
-rw-r--r--sapi/phpdbg/phpdbg_watch.h66
-rw-r--r--sapi/phpdbg/tests/breakpoints_001.phpt22
-rw-r--r--sapi/phpdbg/tests/breakpoints_002.phpt28
-rw-r--r--sapi/phpdbg/tests/breakpoints_003.phpt14
-rw-r--r--sapi/phpdbg/tests/breakpoints_004.phpt28
-rw-r--r--sapi/phpdbg/tests/bug73794.phpt11
-rw-r--r--sapi/phpdbg/tests/exceptions_001.phpt6
-rw-r--r--sapi/phpdbg/tests/exceptions_003.phpt2
-rw-r--r--sapi/phpdbg/tests/finish_leave_001.phpt2
-rw-r--r--sapi/phpdbg/tests/stepping_001.phpt2
-rw-r--r--sapi/phpdbg/tests/watch_001.phpt6
-rw-r--r--sapi/phpdbg/tests/watch_002.phpt32
-rw-r--r--sapi/phpdbg/tests/watch_003.phpt39
-rw-r--r--sapi/phpdbg/tests/watch_004.phpt38
-rw-r--r--sapi/phpdbg/tests/watch_005.phpt48
-rw-r--r--sapi/phpdbg/tests/watch_006.phpt71
81 files changed, 2443 insertions, 1379 deletions
diff --git a/sapi/apache2handler/php_apache.h b/sapi/apache2handler/php_apache.h
index 38e4716779..72238f2e87 100644
--- a/sapi/apache2handler/php_apache.h
+++ b/sapi/apache2handler/php_apache.h
@@ -24,10 +24,16 @@
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
+#include "http_log.h"
#include "php.h"
#include "main/php_streams.h"
+/* Enable per-module logging in Apache 2.4+ */
+#ifdef APLOG_USE_MODULE
+APLOG_USE_MODULE(php7);
+#endif
+
/* Declare this so we can get to it from outside the sapi_apache2.c file */
extern module AP_MODULE_DECLARE_DATA php7_module;
diff --git a/sapi/apache2handler/sapi_apache2.c b/sapi/apache2handler/sapi_apache2.c
index bf22d2c783..5ec248a02d 100644
--- a/sapi/apache2handler/sapi_apache2.c
+++ b/sapi/apache2handler/sapi_apache2.c
@@ -314,16 +314,52 @@ php_apache_sapi_flush(void *server_context)
}
}
-static void php_apache_sapi_log_message(char *msg)
+static void php_apache_sapi_log_message(char *msg, int syslog_type_int)
{
php_struct *ctx;
+ int aplog_type = APLOG_ERR;
ctx = SG(server_context);
+ switch (syslog_type_int) {
+#if LOG_EMERG != LOG_CRIT
+ case LOG_EMERG:
+ aplog_type = APLOG_EMERG;
+ break;
+#endif
+#if LOG_ALERT != LOG_CRIT
+ case LOG_ALERT:
+ aplog_type = APLOG_ALERT;
+ break;
+#endif
+ case LOG_CRIT:
+ aplog_type = APLOG_CRIT;
+ break;
+ case LOG_ERR:
+ aplog_type = APLOG_ERR;
+ break;
+ case LOG_WARNING:
+ aplog_type = APLOG_WARNING;
+ break;
+ case LOG_NOTICE:
+ aplog_type = APLOG_NOTICE;
+ break;
+#if LOG_INFO != LOG_NOTICE
+ case LOG_INFO:
+ aplog_type = APLOG_INFO;
+ break;
+#endif
+#if LOG_NOTICE != LOG_DEBUG
+ case LOG_DEBUG:
+ aplog_type = APLOG_DEBUG;
+ break;
+#endif
+ }
+
if (ctx == NULL) { /* we haven't initialized our ctx yet, oh well */
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL, "%s", msg);
} else {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "%s", msg);
+ ap_log_rerror(APLOG_MARK, aplog_type, 0, ctx->r, "%s", msg);
}
}
@@ -332,7 +368,7 @@ static void php_apache_sapi_log_message_ex(char *msg, request_rec *r)
if (r) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, msg, r->filename);
} else {
- php_apache_sapi_log_message(msg);
+ php_apache_sapi_log_message(msg, -1);
}
}
@@ -456,9 +492,7 @@ php_apache_server_startup(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp
ZEND_TSRMLS_CACHE_UPDATE();
#endif
-#ifdef ZEND_SIGNALS
zend_signal_startup();
-#endif
sapi_startup(&apache2_sapi_module);
apache2_sapi_module.startup(&apache2_sapi_module);
diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c
index 896635711b..ef75d980dd 100644
--- a/sapi/cgi/cgi_main.c
+++ b/sapi/cgi/cgi_main.c
@@ -35,6 +35,7 @@
#ifdef PHP_WIN32
# include "win32/time.h"
# include "win32/signal.h"
+# include "win32/winutil.h"
# include <process.h>
#endif
@@ -102,7 +103,6 @@ struct sigaction act, old_term, old_quit, old_int;
static void (*php_php_import_environment_variables)(zval *array_ptr);
-#ifndef PHP_WIN32
/* these globals used for forking children on unix systems */
/**
* Number of child processes that will get created to service requests
@@ -115,6 +115,7 @@ static int children = 0;
*/
static int parent = 1;
+#ifndef PHP_WIN32
/* Did parent received exit signals SIG_TERM/SIG_INT/SIG_QUIT */
static int exit_signal = 0;
@@ -222,6 +223,14 @@ static php_cgi_globals_struct php_cgi_globals;
#define TRANSLATE_SLASHES(path)
#endif
+#ifdef PHP_WIN32
+#define WIN32_MAX_SPAWN_CHILDREN 64
+HANDLE kid_cgi_ps[WIN32_MAX_SPAWN_CHILDREN];
+int kids;
+HANDLE job = NULL;
+JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = { 0 };
+#endif
+
#ifndef HAVE_ATTRIBUTE_WEAK
static void fcgi_log(int type, const char *format, ...) {
va_list ap;
@@ -267,8 +276,9 @@ static int print_extension_info(zend_extension *ext, void *arg)
static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s)
{
- return strcmp( ((zend_extension *)(*f)->data)->name,
- ((zend_extension *)(*s)->data)->name);
+ zend_extension *fe = (zend_extension*)(*f)->data;
+ zend_extension *se = (zend_extension*)(*s)->data;
+ return strcmp(fe->name, se->name);
}
static void print_extensions(void)
@@ -354,9 +364,7 @@ static void sapi_fcgi_flush(void *server_context)
fcgi_request *request = (fcgi_request*) server_context;
if (
-#ifndef PHP_WIN32
!parent &&
-#endif
request && !fcgi_flush(request, 0)) {
php_handle_aborted_connection();
@@ -703,7 +711,7 @@ static void sapi_cgi_register_variables(zval *track_vars_array)
}
}
-static void sapi_cgi_log_message(char *message)
+static void sapi_cgi_log_message(char *message, int syslog_type_int)
{
if (fcgi_is_fastcgi() && CGIG(fcgi_logging)) {
fcgi_request *request;
@@ -900,9 +908,7 @@ static int sapi_cgi_deactivate(void)
if (SG(sapi_started)) {
if (fcgi_is_fastcgi()) {
if (
-#ifndef PHP_WIN32
!parent &&
-#endif
!fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
php_handle_aborted_connection();
}
@@ -964,7 +970,7 @@ ZEND_END_ARG_INFO()
static const zend_function_entry additional_functions[] = {
ZEND_FE(dl, arginfo_dl)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* {{{ php_cgi_usage
@@ -1431,6 +1437,29 @@ void fastcgi_cleanup(int signal)
exit(0);
}
}
+#else
+BOOL WINAPI fastcgi_cleanup(DWORD sig)
+{
+ int i = kids;
+
+ while (0 < i--) {
+ if (NULL == kid_cgi_ps[i]) {
+ continue;
+ }
+
+ TerminateProcess(kid_cgi_ps[i], 0);
+ CloseHandle(kid_cgi_ps[i]);
+ kid_cgi_ps[i] = NULL;
+ }
+
+ if (job) {
+ CloseHandle(job);
+ }
+
+ parent = 0;
+
+ return TRUE;
+}
#endif
PHP_INI_BEGIN()
@@ -1688,7 +1717,7 @@ const zend_function_entry cgi_functions[] = {
PHP_FE(apache_request_headers, arginfo_no_args)
PHP_FE(apache_response_headers, arginfo_no_args)
PHP_FALIAS(getallheaders, apache_request_headers, arginfo_no_args)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
static zend_module_entry cgi_module_entry = {
@@ -1772,9 +1801,7 @@ int main(int argc, char *argv[])
ZEND_TSRMLS_CACHE_UPDATE();
#endif
-#ifdef ZEND_SIGNALS
zend_signal_startup();
-#endif
#ifdef ZTS
ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
@@ -1979,8 +2006,7 @@ consult the installation file that came with this distribution, or visit \n\
/* library is already initialized, now init our request */
request = fcgi_init_request(fcgi_fd, NULL, NULL, NULL);
-#ifndef PHP_WIN32
- /* Pre-fork, if required */
+ /* Pre-fork or spawn, if required */
if (getenv("PHP_FCGI_CHILDREN")) {
char * children_str = getenv("PHP_FCGI_CHILDREN");
children = atoi(children_str);
@@ -1992,10 +2018,27 @@ consult the installation file that came with this distribution, or visit \n\
/* This is the number of concurrent requests, equals FCGI_MAX_CONNS */
fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, children_str, strlen(children_str));
} else {
+#ifdef PHP_WIN32
+ /* If this env var is set, the process was invoked as a child. Let
+ it show the original PHP_FCGI_CHILDREN value, while don't care
+ otherwise. */
+ char * children_str = getenv("PHP_FCGI_CHILDREN_FOR_KID");
+ if (children_str) {
+ char putenv_buf[sizeof("PHP_FCGI_CHILDREN")+5];
+
+ snprintf(putenv_buf, sizeof(putenv_buf), "%s=%s", "PHP_FCGI_CHILDREN", children_str);
+ putenv(putenv_buf);
+ putenv("PHP_FCGI_CHILDREN_FOR_KID=");
+
+ SetEnvironmentVariable("PHP_FCGI_CHILDREN", children_str);
+ SetEnvironmentVariable("PHP_FCGI_CHILDREN_FOR_KID", NULL);
+ }
+#endif
fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, "1", sizeof("1")-1);
fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, "1", sizeof("1")-1);
}
+#ifndef PHP_WIN32
if (children) {
int running = 0;
pid_t pid;
@@ -2040,6 +2083,7 @@ consult the installation file that came with this distribution, or visit \n\
sigaction(SIGTERM, &old_term, 0);
sigaction(SIGQUIT, &old_quit, 0);
sigaction(SIGINT, &old_int, 0);
+ zend_signal_init();
break;
case -1:
perror("php (pre-forking)");
@@ -2079,8 +2123,126 @@ consult the installation file that came with this distribution, or visit \n\
}
} else {
parent = 0;
+ zend_signal_init();
}
+#else
+ if (children) {
+ wchar_t *cmd_line_tmp, cmd_line[PHP_WIN32_IOUTIL_MAXPATHLEN];
+ size_t cmd_line_len;
+ char kid_buf[16];
+ int i;
+
+ ZeroMemory(&kid_cgi_ps, sizeof(kid_cgi_ps));
+ kids = children < WIN32_MAX_SPAWN_CHILDREN ? children : WIN32_MAX_SPAWN_CHILDREN;
+
+ SetConsoleCtrlHandler(fastcgi_cleanup, TRUE);
+
+ /* kids will inherit the env, don't let them spawn */
+ SetEnvironmentVariable("PHP_FCGI_CHILDREN", NULL);
+ /* instead, set a temporary env var, so then the child can read and
+ show the actual setting correctly. */
+ snprintf(kid_buf, 16, "%d", children);
+ SetEnvironmentVariable("PHP_FCGI_CHILDREN_FOR_KID", kid_buf);
+
+ /* The current command line is used as is. This should normally be no issue,
+ even if there were some I/O redirection. If some issues turn out, an
+ extra parsing might be needed here. */
+ cmd_line_tmp = GetCommandLineW();
+ if (!cmd_line_tmp) {
+ DWORD err = GetLastError();
+ char *err_text = php_win32_error_to_msg(err);
+
+ fprintf(stderr, "unable to get current command line: [0x%08lx]: %s\n", err, err_text);
+
+ goto parent_out;
+ }
+
+ cmd_line_len = wcslen(cmd_line_tmp);
+ if (cmd_line_len > sizeof(cmd_line) - 1) {
+ fprintf(stderr, "command line is too long\n");
+ goto parent_out;
+ }
+ memmove(cmd_line, cmd_line_tmp, (cmd_line_len + 1)*sizeof(wchar_t));
+
+ job = CreateJobObject(NULL, NULL);
+ if (!job) {
+ DWORD err = GetLastError();
+ char *err_text = php_win32_error_to_msg(err);
+
+ fprintf(stderr, "unable to create job object: [0x%08lx]: %s\n", err, err_text);
+
+ goto parent_out;
+ }
+
+ job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info))) {
+ DWORD err = GetLastError();
+ char *err_text = php_win32_error_to_msg(err);
+
+ fprintf(stderr, "unable to configure job object: [0x%08lx]: %s\n", err, err_text);
+ }
+
+ while (parent) {
+ i = kids;
+ while (0 < i--) {
+ DWORD status;
+
+ if (NULL != kid_cgi_ps[i]) {
+ if(!GetExitCodeProcess(kid_cgi_ps[i], &status) || status != STILL_ACTIVE) {
+ CloseHandle(kid_cgi_ps[i]);
+ kid_cgi_ps[i] = NULL;
+ }
+ }
+ }
+
+ i = kids;
+ while (0 < i--) {
+ PROCESS_INFORMATION pi;
+ STARTUPINFOW si;
+
+ if (NULL != kid_cgi_ps[i]) {
+ continue;
+ }
+
+ ZeroMemory(&si, sizeof(si));
+ si.cb = sizeof(si);
+ ZeroMemory(&pi, sizeof(pi));
+
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdOutput = INVALID_HANDLE_VALUE;
+ si.hStdInput = (HANDLE)_get_osfhandle(fcgi_fd);
+ si.hStdError = INVALID_HANDLE_VALUE;
+
+ if (CreateProcessW(NULL, cmd_line, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
+ kid_cgi_ps[i] = pi.hProcess;
+ if (!AssignProcessToJobObject(job, pi.hProcess)) {
+ DWORD err = GetLastError();
+ char *err_text = php_win32_error_to_msg(err);
+
+ fprintf(stderr, "unable to assign child process to job object: [0x%08lx]: %s\n", err, err_text);
+ }
+ CloseHandle(pi.hThread);
+ } else {
+ DWORD err = GetLastError();
+ char *err_text = php_win32_error_to_msg(err);
+
+ kid_cgi_ps[i] = NULL;
+
+ fprintf(stderr, "unable to spawn: [0x%08lx]: %s\n", err, err_text);
+ }
+ }
+
+ WaitForMultipleObjects(kids, kid_cgi_ps, FALSE, INFINITE);
+ }
+
+ /* restore my env */
+ SetEnvironmentVariable("PHP_FCGI_CHILDREN", kid_buf);
+
+ goto parent_out;
+ } else {
+ parent = 0;
+ }
#endif /* WIN32 */
}
@@ -2584,9 +2746,7 @@ out:
#endif
}
-#ifndef PHP_WIN32
parent_out:
-#endif
SG(server_context) = NULL;
php_module_shutdown();
diff --git a/sapi/cli/config.w32 b/sapi/cli/config.w32
index 664394c8a6..7d0e5954ff 100644
--- a/sapi/cli/config.w32
+++ b/sapi/cli/config.w32
@@ -8,10 +8,16 @@ ARG_ENABLE('cli-win32', 'Build console-less CLI version of PHP', 'no');
if (PHP_CLI == "yes") {
SAPI('cli', 'php_cli.c php_http_parser.c php_cli_server.c php_cli_process_title.c ps_title.c', 'php.exe', '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');
ADD_FLAG("LIBS_CLI", "ws2_32.lib");
+ ADD_FLAG("LIBS_CLI", "shell32.lib");
if (PHP_CRT_DEBUG == "yes") {
ADD_FLAG("CFLAGS_CLI", "/D PHP_WIN32_DEBUG_HEAP");
}
ADD_FLAG("LDFLAGS_CLI", "/stack:67108864");
+
+ if (CHECK_LIB("edit_a.lib;edit.lib", "cli", PHP_CLI) &&
+ CHECK_HEADER_ADD_INCLUDE("editline/readline.h", "CFLAGS_CLI")) {
+ ADD_FLAG("CFLAGS_CLI", "/D HAVE_LIBEDIT");
+ }
}
if (PHP_CLI_WIN32 == "yes") {
diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c
index 1ae15e96ab..7096829c63 100644
--- a/sapi/cli/php_cli.c
+++ b/sapi/cli/php_cli.c
@@ -39,6 +39,7 @@
#include "win32/time.h"
#include "win32/signal.h"
#include <process.h>
+#include <shellapi.h>
#endif
#if HAVE_SYS_TIME_H
#include <sys/time.h>
@@ -102,9 +103,12 @@ PHPAPI extern char *php_ini_opened_path;
PHPAPI extern char *php_ini_scanned_path;
PHPAPI extern char *php_ini_scanned_files;
-#if defined(PHP_WIN32) && defined(ZTS)
+#if defined(PHP_WIN32)
+#if defined(ZTS)
ZEND_TSRMLS_CACHE_DEFINE()
#endif
+static DWORD orig_cp = 0;
+#endif
#ifndef O_BINARY
#define O_BINARY 0
@@ -218,8 +222,9 @@ static int print_extension_info(zend_extension *ext, void *arg) /* {{{ */
static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s) /* {{{ */
{
- return strcmp(((zend_extension *)(*f)->data)->name,
- ((zend_extension *)(*s)->data)->name);
+ zend_extension *fe = (zend_extension*)(*f)->data;
+ zend_extension *se = (zend_extension*)(*s)->data;
+ return strcmp(fe->name, se->name);
}
/* }}} */
@@ -267,11 +272,7 @@ PHP_CLI_API size_t sapi_cli_single_write(const char *str, size_t str_length) /*
#endif
if (cli_shell_callbacks.cli_shell_write) {
- size_t shell_wrote;
- shell_wrote = cli_shell_callbacks.cli_shell_write(str, str_length);
- if (shell_wrote > -1) {
- return shell_wrote;
- }
+ cli_shell_callbacks.cli_shell_write(str, str_length);
}
#ifdef PHP_WRITE_STDOUT
@@ -376,7 +377,7 @@ static void sapi_cli_register_variables(zval *track_vars_array) /* {{{ */
}
/* }}} */
-static void sapi_cli_log_message(char *message) /* {{{ */
+static void sapi_cli_log_message(char *message, int syslog_type_int) /* {{{ */
{
fprintf(stderr, "%s\n", message);
#ifdef PHP_WIN32
@@ -490,7 +491,7 @@ static const zend_function_entry additional_functions[] = {
ZEND_FE(dl, arginfo_dl)
PHP_FE(cli_set_process_title, arginfo_cli_set_process_title)
PHP_FE(cli_get_process_title, arginfo_cli_get_process_title)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
/* {{{ php_cli_usage
@@ -560,7 +561,6 @@ static php_stream *s_in_process = NULL;
static void cli_register_file_handles(void) /* {{{ */
{
- zval zin, zout, zerr;
php_stream *s_in, *s_out, *s_err;
php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
zend_constant ic, oc, ec;
@@ -584,23 +584,20 @@ static void cli_register_file_handles(void) /* {{{ */
s_in_process = s_in;
- php_stream_to_zval(s_in, &zin);
- php_stream_to_zval(s_out, &zout);
- php_stream_to_zval(s_err, &zerr);
+ php_stream_to_zval(s_in, &ic.value);
+ php_stream_to_zval(s_out, &oc.value);
+ php_stream_to_zval(s_err, &ec.value);
- ZVAL_COPY_VALUE(&ic.value, &zin);
ic.flags = CONST_CS;
ic.name = zend_string_init("STDIN", sizeof("STDIN")-1, 1);
ic.module_number = 0;
zend_register_constant(&ic);
- ZVAL_COPY_VALUE(&oc.value, &zout);
oc.flags = CONST_CS;
oc.name = zend_string_init("STDOUT", sizeof("STDOUT")-1, 1);
oc.module_number = 0;
zend_register_constant(&oc);
- ZVAL_COPY_VALUE(&ec.value, &zerr);
ec.flags = CONST_CS;
ec.name = zend_string_init("STDERR", sizeof("STDERR")-1, 1);
ec.module_number = 0;
@@ -649,6 +646,17 @@ static int cli_seek_file_begin(zend_file_handle *file_handle, char *script_file,
}
/* }}} */
+/*{{{ php_cli_win32_ctrl_handler */
+#if defined(PHP_WIN32) && !defined(PHP_CLI_WIN32_NO_CONSOLE)
+BOOL WINAPI php_cli_win32_ctrl_handler(DWORD sig)
+{
+ (void)php_win32_cp_cli_do_restore(orig_cp);
+
+ return FALSE;
+}
+#endif
+/*}}}*/
+
static int do_cli(int argc, char **argv) /* {{{ */
{
int c;
@@ -692,6 +700,14 @@ static int do_cli(int argc, char **argv) /* {{{ */
#else
"NTS "
#endif
+#ifdef COMPILER
+ COMPILER
+ " "
+#endif
+#ifdef ARCHITECTURE
+ ARCHITECTURE
+ " "
+#endif
#if ZEND_DEBUG
"DEBUG "
#endif
@@ -1166,9 +1182,16 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
int main(int argc, char *argv[])
#endif
{
-#ifdef PHP_CLI_WIN32_NO_CONSOLE
+#if defined(PHP_WIN32)
+# ifdef PHP_CLI_WIN32_NO_CONSOLE
int argc = __argc;
char **argv = __argv;
+# else
+ int num_args;
+ wchar_t **argv_wide;
+ char **argv_save = argv;
+ BOOL using_wide_argv = 0;
+# endif
#endif
int c;
@@ -1225,9 +1248,7 @@ int main(int argc, char *argv[])
ZEND_TSRMLS_CACHE_UPDATE();
#endif
-#ifdef ZEND_SIGNALS
zend_signal_startup();
-#endif
#ifdef PHP_WIN32
_fmode = _O_BINARY; /*sets default for file streams to binary */
@@ -1335,6 +1356,19 @@ exit_loop:
}
module_started = 1;
+#if defined(PHP_WIN32) && !defined(PHP_CLI_WIN32_NO_CONSOLE)
+ php_win32_cp_cli_setup();
+ orig_cp = (php_win32_cp_get_orig())->id;
+ /* Ignore the delivered argv and argc, read from W API. This place
+ might be too late though, but this is the earliest place ATW
+ we can access the internal charset information from PHP. */
+ argv_wide = CommandLineToArgvW(GetCommandLineW(), &num_args);
+ PHP_WIN32_CP_W_TO_ANY_ARRAY(argv_wide, num_args, argv, argc)
+ using_wide_argv = 1;
+
+ SetConsoleCtrlHandler(php_cli_win32_ctrl_handler, TRUE);
+#endif
+
/* -e option */
if (use_extended_info) {
CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
@@ -1368,6 +1402,15 @@ out:
tsrm_shutdown();
#endif
+#if defined(PHP_WIN32) && !defined(PHP_CLI_WIN32_NO_CONSOLE)
+ (void)php_win32_cp_cli_restore();
+
+ if (using_wide_argv) {
+ PHP_WIN32_CP_FREE_ARRAY(argv, argc);
+ LocalFree(argv_wide);
+ }
+ argv = argv_save;
+#endif
/*
* Do not move this de-initialization. It needs to happen right before
* exiting.
diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c
index 39ded2f5b3..fb636d06a3 100644
--- a/sapi/cli/php_cli_server.c
+++ b/sapi/cli/php_cli_server.c
@@ -96,6 +96,7 @@
#include "ext/standard/html.h"
#include "ext/standard/url.h" /* for php_raw_url_decode() */
#include "ext/standard/php_string.h" /* for php_dirname() */
+#include "ext/date/php_date.h" /* for php_format_date() */
#include "php_network.h"
#include "php_http_parser.h"
@@ -342,15 +343,24 @@ static void append_http_status_line(smart_str *buffer, int protocol_version, int
static void append_essential_headers(smart_str* buffer, php_cli_server_client *client, int persistent) /* {{{ */
{
- {
- char *val;
- if (NULL != (val = zend_hash_str_find_ptr(&client->request.headers, "host", sizeof("host")-1))) {
- smart_str_appendl_ex(buffer, "Host", sizeof("Host") - 1, persistent);
- smart_str_appendl_ex(buffer, ": ", sizeof(": ") - 1, persistent);
- smart_str_appends_ex(buffer, val, persistent);
- smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
- }
+ char *val;
+ struct timeval tv = {0};
+
+ if (NULL != (val = zend_hash_str_find_ptr(&client->request.headers, "host", sizeof("host")-1))) {
+ smart_str_appendl_ex(buffer, "Host", sizeof("Host") - 1, persistent);
+ smart_str_appendl_ex(buffer, ": ", sizeof(": ") - 1, persistent);
+ smart_str_appends_ex(buffer, val, persistent);
+ smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
}
+
+ if (!gettimeofday(&tv, NULL)) {
+ zend_string *dt = php_format_date("r", 1, tv.tv_sec, 1);
+ smart_str_appendl_ex(buffer, "Date: ", 6, persistent);
+ smart_str_appends_ex(buffer, dt->val, persistent);
+ smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
+ zend_string_release(dt);
+ }
+
smart_str_appendl_ex(buffer, "Connection: close\r\n", sizeof("Connection: close\r\n") - 1, persistent);
} /* }}} */
@@ -475,7 +485,7 @@ const zend_function_entry server_additional_functions[] = {
PHP_FE(apache_request_headers, arginfo_no_args)
PHP_FE(apache_response_headers, arginfo_no_args)
PHP_FALIAS(getallheaders, apache_request_headers, arginfo_no_args)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
static int sapi_cli_server_startup(sapi_module_struct *sapi_module) /* {{{ */
@@ -682,7 +692,7 @@ static void sapi_cli_server_register_variables(zval *track_vars_array) /* {{{ */
zend_hash_apply_with_arguments(&client->request.headers, (apply_func_args_t)sapi_cli_server_register_entry_cb, 1, track_vars_array);
} /* }}} */
-static void sapi_cli_server_log_message(char *msg) /* {{{ */
+static void sapi_cli_server_log_message(char *msg, int syslog_type_int) /* {{{ */
{
char buf[52];
@@ -995,7 +1005,11 @@ static int php_cli_server_content_sender_send(php_cli_server_content_sender *sen
if (nbytes_sent < 0) {
*nbytes_sent_total = _nbytes_sent_total;
return php_socket_errno();
+#ifdef PHP_WIN32
} else if (nbytes_sent == chunk->data.heap.len) {
+#else
+ } else if (nbytes_sent == (ssize_t)chunk->data.heap.len) {
+#endif
php_cli_server_chunk_dtor(chunk);
pefree(chunk, 1);
sender->buffer.first = next;
@@ -1018,7 +1032,11 @@ static int php_cli_server_content_sender_send(php_cli_server_content_sender *sen
if (nbytes_sent < 0) {
*nbytes_sent_total = _nbytes_sent_total;
return php_socket_errno();
+#ifdef PHP_WIN32
} else if (nbytes_sent == chunk->data.immortal.len) {
+#else
+ } else if (nbytes_sent == (ssize_t)chunk->data.immortal.len) {
+#endif
php_cli_server_chunk_dtor(chunk);
pefree(chunk, 1);
sender->buffer.first = next;
@@ -1169,7 +1187,7 @@ static void php_cli_server_logf(const char *format, ...) /* {{{ */
}
if (sapi_module.log_message) {
- sapi_module.log_message(buf);
+ sapi_module.log_message(buf, -1);
}
efree(buf);
@@ -1385,7 +1403,7 @@ static void php_cli_server_request_translate_vpath(php_cli_server_request *reque
*p = '\0';
q = p;
while (q > buf) {
- if (!zend_stat(buf, &sb)) {
+ if (!php_sys_stat(buf, &sb)) {
if (sb.st_mode & S_IFDIR) {
const char **file = index_files;
if (q[-1] != DEFAULT_SLASH) {
@@ -1394,7 +1412,7 @@ static void php_cli_server_request_translate_vpath(php_cli_server_request *reque
while (*file) {
size_t l = strlen(*file);
memmove(q, *file, l + 1);
- if (!zend_stat(buf, &sb) && (sb.st_mode & S_IFREG)) {
+ if (!php_sys_stat(buf, &sb) && (sb.st_mode & S_IFREG)) {
q += l;
break;
}
@@ -1748,7 +1766,7 @@ static int php_cli_server_client_read_request(php_cli_server_client *client, cha
}
client->parser.data = client;
nbytes_consumed = php_http_parser_execute(&client->parser, &settings, buf, nbytes_read);
- if (nbytes_consumed != nbytes_read) {
+ if (nbytes_consumed != (size_t)nbytes_read) {
if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {
*errstr = estrdup("Unsupported SSL request");
} else {
@@ -1905,7 +1923,7 @@ static int php_cli_server_send_error_page(php_cli_server *server, php_cli_server
if (!chunk) {
goto fail;
}
- snprintf(chunk->data.heap.p, chunk->data.heap.len, prologue_template, status, status_string, ZSTR_VAL(escaped_request_uri));
+ snprintf(chunk->data.heap.p, chunk->data.heap.len, prologue_template, status, status_string);
chunk->data.heap.len = strlen(chunk->data.heap.p);
php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
}
@@ -2545,7 +2563,7 @@ int do_cli_server(int argc, char **argv) /* {{{ */
if (document_root) {
zend_stat_t sb;
- if (zend_stat(document_root, &sb)) {
+ if (php_sys_stat(document_root, &sb)) {
fprintf(stderr, "Directory %s does not exist.\n", document_root);
return 1;
}
@@ -2592,6 +2610,7 @@ int do_cli_server(int argc, char **argv) /* {{{ */
#if defined(HAVE_SIGNAL_H) && defined(SIGINT)
signal(SIGINT, php_cli_server_sigint_handler);
+ zend_signal_init();
#endif
php_cli_server_do_event_loop(&server);
php_cli_server_dtor(&server);
diff --git a/sapi/cli/php_http_parser.c b/sapi/cli/php_http_parser.c
index 5f8eb3ce0b..59361d0911 100644
--- a/sapi/cli/php_http_parser.c
+++ b/sapi/cli/php_http_parser.c
@@ -1539,7 +1539,7 @@ size_t php_http_parser_execute (php_http_parser *parser,
p += to_read - 1;
}
- if (to_read == parser->content_length) {
+ if (to_read == (size_t)parser->content_length) {
state = s_chunk_data_almost_done;
}
diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c
index 15ab0445d8..303115f7bc 100644
--- a/sapi/cli/ps_title.c
+++ b/sapi/cli/ps_title.c
@@ -42,6 +42,7 @@
#include "config.w32.h"
#include <windows.h>
#include <process.h>
+#include "win32/codepage.h"
#else
#include "php_config.h"
extern char** environ;
@@ -109,8 +110,6 @@ extern char** environ;
static char windows_error_details[64];
static char ps_buffer[MAX_PATH];
static const size_t ps_buffer_size = MAX_PATH;
-typedef BOOL (WINAPI *MySetConsoleTitle)(LPCTSTR);
-typedef DWORD (WINAPI *MyGetConsoleTitle)(LPTSTR, DWORD);
#elif defined(PS_USE_CLOBBER_ARGV)
static char *ps_buffer; /* will point to argv area */
static size_t ps_buffer_size; /* space determined at run time */
@@ -225,9 +224,9 @@ char** save_ps_args(int argc, char** argv)
{
new_argv[i] = strdup(argv[i]);
if (!new_argv[i]) {
- free(new_argv);
+ free(new_argv);
goto clobber_error;
- }
+ }
}
new_argv[argc] = NULL;
@@ -371,22 +370,13 @@ int set_ps_title(const char* title)
#ifdef PS_USE_WIN32
{
- MySetConsoleTitle set_title = NULL;
- HMODULE hMod = LoadLibrary("kernel32.dll");
+ wchar_t *ps_buffer_w = php_win32_cp_any_to_w(ps_buffer);
- if (!hMod) {
+ if (!ps_buffer_w || !SetConsoleTitleW(ps_buffer_w)) {
return PS_TITLE_WINDOWS_ERROR;
}
- /* NOTE we don't use _UNICODE*/
- set_title = (MySetConsoleTitle)GetProcAddress(hMod, "SetConsoleTitleA");
- if (!set_title) {
- return PS_TITLE_WINDOWS_ERROR;
- }
-
- if (!set_title(ps_buffer)) {
- return PS_TITLE_WINDOWS_ERROR;
- }
+ free(ps_buffer_w);
}
#endif /* PS_USE_WIN32 */
@@ -407,22 +397,22 @@ int get_ps_title(int *displen, const char** string)
#ifdef PS_USE_WIN32
{
- MyGetConsoleTitle get_title = NULL;
- HMODULE hMod = LoadLibrary("kernel32.dll");
+ wchar_t ps_buffer_w[MAX_PATH];
+ char *tmp;
- if (!hMod) {
+ if (!(ps_buffer_cur_len = GetConsoleTitleW(ps_buffer_w, (DWORD)sizeof(ps_buffer_w)))) {
return PS_TITLE_WINDOWS_ERROR;
}
- /* NOTE we don't use _UNICODE*/
- get_title = (MyGetConsoleTitle)GetProcAddress(hMod, "GetConsoleTitleA");
- if (!get_title) {
+ tmp = php_win32_cp_conv_w_to_any(ps_buffer_w, PHP_WIN32_CP_IGNORE_LEN, &ps_buffer_cur_len);
+ if (!tmp) {
return PS_TITLE_WINDOWS_ERROR;
}
- if (!(ps_buffer_cur_len = get_title(ps_buffer, (DWORD)ps_buffer_size))) {
- return PS_TITLE_WINDOWS_ERROR;
- }
+ ps_buffer_cur_len = ps_buffer_cur_len > sizeof(ps_buffer)-1 ? sizeof(ps_buffer)-1 : ps_buffer_cur_len;
+
+ memmove(ps_buffer, tmp, ps_buffer_cur_len);
+ free(tmp);
}
#endif
*displen = (int)ps_buffer_cur_len;
diff --git a/sapi/cli/tests/017.phpt b/sapi/cli/tests/017.phpt
index efaf977db4..dbddb283a4 100644
--- a/sapi/cli/tests/017.phpt
+++ b/sapi/cli/tests/017.phpt
@@ -6,6 +6,9 @@ include "skipif.inc";
if (!extension_loaded('readline') || readline_info('done') !== NULL) {
die ("skip need readline support using libedit");
}
+if(substr(PHP_OS, 0, 3) == 'WIN' ) {
+ die('skip not for Windows');
+}
?>
--FILE--
<?php
diff --git a/sapi/cli/tests/argv_mb.phpt b/sapi/cli/tests/argv_mb.phpt
new file mode 100644
index 0000000000..e8c47f0972
--- /dev/null
+++ b/sapi/cli/tests/argv_mb.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Test basic argv multibyte API integration
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die ("skip only for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+$argv_fl = dirname(__FILE__) . DIRECTORY_SEPARATOR . "argv_test.php";
+file_put_contents($argv_fl, "<?php var_dump(\$argv); ?>");
+
+var_dump(`$php -n $argv_fl 多字节字符串 マルチバイト文字列 многобайтоваястрока flerbytesträng`);
+
+@unlink($argv_fl);
+
+?>
+==DONE==
+--EXPECTF--
+string(%d) "array(%d) {
+ [0]=>
+ string(%d) "%sargv_test.php"
+ [1]=>
+ string(18) "多字节字符串"
+ [2]=>
+ string(27) "マルチバイト文字列"
+ [3]=>
+ string(38) "многобайтоваястрока"
+ [4]=>
+ string(15) "flerbytesträng"
+}
+"
+==DONE==
diff --git a/sapi/cli/tests/bug43177.phpt b/sapi/cli/tests/bug43177.phpt
index 23af545908..e475fb09b4 100644
--- a/sapi/cli/tests/bug43177.phpt
+++ b/sapi/cli/tests/bug43177.phpt
@@ -61,6 +61,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: localhost
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
@@ -68,18 +69,21 @@ Content-type: text/html; charset=UTF-8
OK
HTTP/1.0 500 Internal Server Error
Host: localhost
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
HTTP/1.0 500 Internal Server Error
Host: localhost
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
HTTP/1.0 500 Internal Server Error
Host: localhost
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug65066_100.phpt b/sapi/cli/tests/bug65066_100.phpt
index 901ba188fd..34381d8e96 100644
--- a/sapi/cli/tests/bug65066_100.phpt
+++ b/sapi/cli/tests/bug65066_100.phpt
@@ -34,6 +34,7 @@ HEADER
--EXPECTF--
HTTP/1.1 100 Continue
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug65066_422.phpt b/sapi/cli/tests/bug65066_422.phpt
index 4e5d31c7a7..f25ddfbca6 100644
--- a/sapi/cli/tests/bug65066_422.phpt
+++ b/sapi/cli/tests/bug65066_422.phpt
@@ -34,6 +34,7 @@ HEADER
--EXPECTF--
HTTP/1.1 422 Unknown Status Code
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug65066_511.phpt b/sapi/cli/tests/bug65066_511.phpt
index a0b4eae393..27c9f9755a 100644
--- a/sapi/cli/tests/bug65066_511.phpt
+++ b/sapi/cli/tests/bug65066_511.phpt
@@ -34,6 +34,7 @@ HEADER
--EXPECTF--
HTTP/1.1 511 Network Authentication Required
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug65633.phpt b/sapi/cli/tests/bug65633.phpt
index 456436b1f7..2e9f2796e8 100644
--- a/sapi/cli/tests/bug65633.phpt
+++ b/sapi/cli/tests/bug65633.phpt
@@ -37,6 +37,7 @@ fclose($fp);
?>
--EXPECTF--
HTTP/1.1 200 OK
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug66606_2.phpt b/sapi/cli/tests/bug66606_2.phpt
index 9f539873e1..c47b7736c0 100644
--- a/sapi/cli/tests/bug66606_2.phpt
+++ b/sapi/cli/tests/bug66606_2.phpt
@@ -38,6 +38,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug66830.phpt b/sapi/cli/tests/bug66830.phpt
index 58c07e031a..b21b33627e 100644
--- a/sapi/cli/tests/bug66830.phpt
+++ b/sapi/cli/tests/bug66830.phpt
@@ -37,6 +37,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug67429.phpt b/sapi/cli/tests/bug67429.phpt
index 1c861be899..2e0b6ad627 100644
--- a/sapi/cli/tests/bug67429.phpt
+++ b/sapi/cli/tests/bug67429.phpt
@@ -43,11 +43,13 @@ HEADER
?>
--EXPECTF--
HTTP/1.1 308 Permanent Redirect
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
HTTP/1.1 426 Upgrade Required
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug68745.phpt b/sapi/cli/tests/bug68745.phpt
index 733d7d0900..4cdfe0ae4e 100644
--- a/sapi/cli/tests/bug68745.phpt
+++ b/sapi/cli/tests/bug68745.phpt
@@ -27,6 +27,7 @@ fclose($fp);
?>
--EXPECTF--
HTTP/1.1 200 OK
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/bug71005.phpt b/sapi/cli/tests/bug71005.phpt
index 3a60dcf42f..f02f261fbf 100644
--- a/sapi/cli/tests/bug71005.phpt
+++ b/sapi/cli/tests/bug71005.phpt
@@ -39,6 +39,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_004.phpt b/sapi/cli/tests/php_cli_server_004.phpt
index 8b913f6596..5e3bbec18f 100644
--- a/sapi/cli/tests/php_cli_server_004.phpt
+++ b/sapi/cli/tests/php_cli_server_004.phpt
@@ -38,6 +38,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_005.phpt b/sapi/cli/tests/php_cli_server_005.phpt
index cdd0ae902f..3f9078c8ae 100644
--- a/sapi/cli/tests/php_cli_server_005.phpt
+++ b/sapi/cli/tests/php_cli_server_005.phpt
@@ -50,6 +50,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_006.phpt b/sapi/cli/tests/php_cli_server_006.phpt
index ad6d6c9598..c68f1e51b0 100644
--- a/sapi/cli/tests/php_cli_server_006.phpt
+++ b/sapi/cli/tests/php_cli_server_006.phpt
@@ -34,6 +34,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_007.phpt b/sapi/cli/tests/php_cli_server_007.phpt
index 6420ff5a41..b588b9ee2e 100644
--- a/sapi/cli/tests/php_cli_server_007.phpt
+++ b/sapi/cli/tests/php_cli_server_007.phpt
@@ -34,6 +34,7 @@ HEADER
--EXPECTF--
HTTP/1.1 401 Unauthorized
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
WWW-Authenticate: Digest realm="foo",qop="auth",nonce="XXXXX",opaque="acbd18db4cc2f85cedef654fccc4a4d8"
diff --git a/sapi/cli/tests/php_cli_server_008.phpt b/sapi/cli/tests/php_cli_server_008.phpt
index 01f825a746..58b2485db5 100644
--- a/sapi/cli/tests/php_cli_server_008.phpt
+++ b/sapi/cli/tests/php_cli_server_008.phpt
@@ -54,6 +54,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
@@ -61,6 +62,7 @@ Content-type: text/html; charset=UTF-8
string(8) "HTTP/1.1"
HTTP/1.0 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_009.phpt b/sapi/cli/tests/php_cli_server_009.phpt
index 7f3009b9bd..d63c6abf28 100644
--- a/sapi/cli/tests/php_cli_server_009.phpt
+++ b/sapi/cli/tests/php_cli_server_009.phpt
@@ -78,6 +78,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
@@ -85,6 +86,7 @@ Content-type: text/html; charset=UTF-8
string(8) "/foo/bar"
HTTP/1.0 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_010.phpt b/sapi/cli/tests/php_cli_server_010.phpt
index ce3abeb501..fe8f1a98ca 100644
--- a/sapi/cli/tests/php_cli_server_010.phpt
+++ b/sapi/cli/tests/php_cli_server_010.phpt
@@ -55,6 +55,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
@@ -65,6 +66,7 @@ string(8) "/foo/bar"
string(7) "foo=bar"
HTTP/1.0 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_012.phpt b/sapi/cli/tests/php_cli_server_012.phpt
index 302540f7e6..10b9cdbfe9 100644
--- a/sapi/cli/tests/php_cli_server_012.phpt
+++ b/sapi/cli/tests/php_cli_server_012.phpt
@@ -40,6 +40,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_013.phpt b/sapi/cli/tests/php_cli_server_013.phpt
index 3ea3ea9cad..23269580fb 100644
--- a/sapi/cli/tests/php_cli_server_013.phpt
+++ b/sapi/cli/tests/php_cli_server_013.phpt
@@ -83,6 +83,7 @@ fclose($fp);
HTTP/1.1 404 Not Found
Host: %s
+Date: %s
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: %d
@@ -91,6 +92,7 @@ Content-Length: %d
</head><body><h1>Not Found</h1><p>The requested resource <code class="url">/</code> was not found on this server.</p></body></html>
HTTP/1.1 404 Not Found
Host: %s
+Date: %s
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: %d
@@ -99,6 +101,7 @@ Content-Length: %d
</head><body><h1>Not Found</h1><p>The requested resource <code class="url">/main/style.css</code> was not found on this server.</p></body></html>
HTTP/1.1 404 Not Found
Host: %s
+Date: %s
Connection: close
Content-Type: text/html; charset=UTF-8
Content-Length: %d
diff --git a/sapi/cli/tests/php_cli_server_014.phpt b/sapi/cli/tests/php_cli_server_014.phpt
index 4f812e2f63..7c50a5e30a 100644
--- a/sapi/cli/tests/php_cli_server_014.phpt
+++ b/sapi/cli/tests/php_cli_server_014.phpt
@@ -65,6 +65,7 @@ fclose($fp);
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: %s
@@ -72,6 +73,7 @@ Content-type: %s
done
HTTP/1.1 404 Not Found
Host: %s
+Date: %s
Connection: close
Content-Type: %s
Content-Length: %d
diff --git a/sapi/cli/tests/php_cli_server_015.phpt b/sapi/cli/tests/php_cli_server_015.phpt
index af0a3f65a8..663268aea6 100644
--- a/sapi/cli/tests/php_cli_server_015.phpt
+++ b/sapi/cli/tests/php_cli_server_015.phpt
@@ -41,6 +41,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_017.phpt b/sapi/cli/tests/php_cli_server_017.phpt
index 34e7d5e289..28876dc52b 100644
--- a/sapi/cli/tests/php_cli_server_017.phpt
+++ b/sapi/cli/tests/php_cli_server_017.phpt
@@ -37,6 +37,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_018.phpt b/sapi/cli/tests/php_cli_server_018.phpt
index 44e1292934..acfb0f2ce3 100644
--- a/sapi/cli/tests/php_cli_server_018.phpt
+++ b/sapi/cli/tests/php_cli_server_018.phpt
@@ -37,6 +37,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/php_cli_server_019.phpt b/sapi/cli/tests/php_cli_server_019.phpt
index aeb7a9f891..7be87a86c9 100644
--- a/sapi/cli/tests/php_cli_server_019.phpt
+++ b/sapi/cli/tests/php_cli_server_019.phpt
@@ -41,6 +41,7 @@ fclose($fp);
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: %s
Bar-Foo: Foo
diff --git a/sapi/cli/tests/php_cli_server_020.phpt b/sapi/cli/tests/php_cli_server_020.phpt
index 4ed673d380..766c666911 100644
--- a/sapi/cli/tests/php_cli_server_020.phpt
+++ b/sapi/cli/tests/php_cli_server_020.phpt
@@ -33,6 +33,7 @@ HEADER
--EXPECTF--
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/cli/tests/upload_2G.phpt b/sapi/cli/tests/upload_2G.phpt
index bd36ad6cda..21dacb439a 100644
--- a/sapi/cli/tests/upload_2G.phpt
+++ b/sapi/cli/tests/upload_2G.phpt
@@ -81,6 +81,7 @@ Test
HTTP/1.1 200 OK
Host: %s
+Date: %s
Connection: close
X-Powered-By: PHP/%s
Content-type: text/html; charset=UTF-8
diff --git a/sapi/embed/php_embed.c b/sapi/embed/php_embed.c
index 931ba60534..e444610d77 100644
--- a/sapi/embed/php_embed.c
+++ b/sapi/embed/php_embed.c
@@ -94,7 +94,7 @@ static void php_embed_send_header(sapi_header_struct *sapi_header, void *server_
{
}
-static void php_embed_log_message(char *message)
+static void php_embed_log_message(char *message, int syslog_type_int)
{
fprintf (stderr, "%s\n", message);
}
@@ -177,9 +177,7 @@ EMBED_SAPI_API int php_embed_init(int argc, char **argv)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
-#ifdef ZEND_SIGNALS
zend_signal_startup();
-#endif
sapi_startup(&php_embed_module);
diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c
index 2e5effebc0..b497d2c82c 100644
--- a/sapi/fpm/fpm/fpm_conf.c
+++ b/sapi/fpm/fpm/fpm_conf.c
@@ -857,7 +857,7 @@ static int fpm_conf_process_all_pools() /* {{{ */
/* status */
if (wp->config->pm_status_path && *wp->config->pm_status_path) {
- int i;
+ size_t i;
char *status = wp->config->pm_status_path;
if (*status != '/') {
@@ -881,7 +881,7 @@ static int fpm_conf_process_all_pools() /* {{{ */
/* ping */
if (wp->config->ping_path && *wp->config->ping_path) {
char *ping = wp->config->ping_path;
- int i;
+ size_t i;
if (*ping != '/') {
zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' must start with a '/'", wp->config->name, ping);
@@ -1254,7 +1254,7 @@ static void fpm_conf_ini_parser_include(char *inc, void *arg) /* {{{ */
#ifdef HAVE_GLOB
glob_t g;
#endif
- int i;
+ size_t i;
if (!inc || !arg) return;
if (*error) return; /* We got already an error. Switch to the end. */
@@ -1271,7 +1271,7 @@ static void fpm_conf_ini_parser_include(char *inc, void *arg) /* {{{ */
return;
}
#endif /* GLOB_NOMATCH */
- zlog(ZLOG_ERROR, "Unable to globalize '%s' (ret=%d) from %s at line %d.", inc, i, filename, ini_lineno);
+ zlog(ZLOG_ERROR, "Unable to globalize '%s' (ret=%zd) from %s at line %d.", inc, i, filename, ini_lineno);
*error = 1;
efree(filename);
return;
@@ -1573,7 +1573,7 @@ static void fpm_conf_dump() /* {{{ */
/*
* Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
*/
- zlog(ZLOG_NOTICE, "[General]");
+ zlog(ZLOG_NOTICE, "[global]");
zlog(ZLOG_NOTICE, "\tpid = %s", STR2STR(fpm_global_config.pid_file));
zlog(ZLOG_NOTICE, "\terror_log = %s", STR2STR(fpm_global_config.error_log));
#ifdef HAVE_SYSLOG_H
diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c
index ca45cb1665..2b8e8cf13a 100644
--- a/sapi/fpm/fpm/fpm_events.c
+++ b/sapi/fpm/fpm/fpm_events.c
@@ -291,7 +291,7 @@ int fpm_event_pre_init(char *machanism) /* {{{ */
}
return -1;
}
-/* }} */
+/* }}} */
const char *fpm_event_machanism_name() /* {{{ */
{
@@ -538,4 +538,3 @@ int fpm_event_del(struct fpm_event_s *ev) /* {{{ */
}
/* }}} */
-/* }}} */
diff --git a/sapi/fpm/fpm/fpm_log.c b/sapi/fpm/fpm/fpm_log.c
index 9f63d7a34c..02f8868142 100644
--- a/sapi/fpm/fpm/fpm_log.c
+++ b/sapi/fpm/fpm/fpm_log.c
@@ -267,13 +267,13 @@ int fpm_log_write(char *log_format) /* {{{ */
/* kilobytes */
} else if (!strcasecmp(format, "kilobytes") || !strcasecmp(format, "kilo")) {
if (!test) {
- len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.memory / 1024);
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%zu", proc.memory / 1024);
}
/* megabytes */
} else if (!strcasecmp(format, "megabytes") || !strcasecmp(format, "mega")) {
if (!test) {
- len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.memory / 1024 / 1024);
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%zu", proc.memory / 1024 / 1024);
}
} else {
diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c
index 695839cd9a..ce9b85620b 100644
--- a/sapi/fpm/fpm/fpm_main.c
+++ b/sapi/fpm/fpm/fpm_main.c
@@ -258,8 +258,9 @@ static int print_extension_info(zend_extension *ext, void *arg) /* {{{ */
static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s) /* {{{ */
{
- return strcmp( ((zend_extension *)(*f)->data)->name,
- ((zend_extension *)(*s)->data)->name);
+ zend_extension *fe = (zend_extension*)(*f)->data;
+ zend_extension *se = (zend_extension*)(*s)->data;
+ return strcmp(fe->name, se->name);
}
/* }}} */
@@ -659,7 +660,7 @@ void sapi_cgi_log_fastcgi(int level, char *message, size_t len)
/* {{{ sapi_cgi_log_message
*/
-static void sapi_cgi_log_message(char *message)
+static void sapi_cgi_log_message(char *message, int syslog_type_int)
{
zlog(ZLOG_NOTICE, "PHP message: %s", message);
}
@@ -1548,7 +1549,7 @@ PHP_FUNCTION(fastcgi_finish_request) /* {{{ */
static const zend_function_entry cgi_fcgi_sapi_functions[] = {
PHP_FE(fastcgi_finish_request, NULL)
- {NULL, NULL, NULL}
+ PHP_FE_END
};
static zend_module_entry cgi_module_entry = {
@@ -1611,9 +1612,7 @@ int main(int argc, char *argv[])
tsrm_ls = ts_resource(0);
#endif
-#ifdef ZEND_SIGNALS
zend_signal_startup();
-#endif
sapi_startup(&cgi_sapi_module);
cgi_sapi_module.php_ini_path_override = NULL;
diff --git a/sapi/fpm/fpm/fpm_php_trace.c b/sapi/fpm/fpm/fpm_php_trace.c
index e6482b6380..4ab2db6e3f 100644
--- a/sapi/fpm/fpm/fpm_php_trace.c
+++ b/sapi/fpm/fpm/fpm_php_trace.c
@@ -101,9 +101,9 @@ static int fpm_php_trace_dump(struct fpm_child_s *child, FILE *slowlog) /* {{{ *
return -1;
}
- if (ZEND_CALL_KIND_EX((*call_info) >> 24) == ZEND_CALL_TOP_CODE) {
+ if (ZEND_CALL_KIND_EX((*call_info) >> ZEND_CALL_INFO_SHIFT) == ZEND_CALL_TOP_CODE) {
return 0;
- } else if (ZEND_CALL_KIND_EX(*(call_info) >> 24) == ZEND_CALL_NESTED_CODE) {
+ } else if (ZEND_CALL_KIND_EX(*(call_info) >> ZEND_CALL_INFO_SHIFT) == ZEND_CALL_NESTED_CODE) {
memcpy(buf, "[INCLUDE_OR_EVAL]", sizeof("[INCLUDE_OR_EVAL]"));
} else {
ZEND_ASSERT(0);
diff --git a/sapi/fpm/fpm/fpm_scoreboard.c b/sapi/fpm/fpm/fpm_scoreboard.c
index 5693ce4e49..8b73df895d 100644
--- a/sapi/fpm/fpm/fpm_scoreboard.c
+++ b/sapi/fpm/fpm/fpm_scoreboard.c
@@ -178,7 +178,7 @@ struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(struct fpm_scoreboard_s *s
child_index = fpm_scoreboard_i;
}
- if (child_index < 0 || child_index >= scoreboard->nprocs) {
+ if (child_index < 0 || (unsigned int)child_index >= scoreboard->nprocs) {
return NULL;
}
@@ -272,7 +272,7 @@ void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_ind
return;
}
- if (child_index < 0 || child_index >= scoreboard->nprocs) {
+ if (child_index < 0 || (unsigned int)child_index >= scoreboard->nprocs) {
return;
}
@@ -294,7 +294,7 @@ int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_in
}
/* first try the slot which is supposed to be free */
- if (scoreboard->free_proc >= 0 && scoreboard->free_proc < scoreboard->nprocs) {
+ if (scoreboard->free_proc >= 0 && (unsigned int)scoreboard->free_proc < scoreboard->nprocs) {
if (scoreboard->procs[scoreboard->free_proc] && !scoreboard->procs[scoreboard->free_proc]->used) {
i = scoreboard->free_proc;
}
@@ -302,7 +302,7 @@ int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_in
if (i < 0) { /* the supposed free slot is not, let's search for a free slot */
zlog(ZLOG_DEBUG, "[pool %s] the proc->free_slot was not free. Let's search", scoreboard->pool);
- for (i = 0; i < scoreboard->nprocs; i++) {
+ for (i = 0; i < (int)scoreboard->nprocs; i++) {
if (scoreboard->procs[i] && !scoreboard->procs[i]->used) { /* found */
break;
}
@@ -310,7 +310,7 @@ int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_in
}
/* no free slot */
- if (i < 0 || i >= scoreboard->nprocs) {
+ if (i < 0 || i >= (int)scoreboard->nprocs) {
zlog(ZLOG_ERROR, "[pool %s] no free scoreboard slot", scoreboard->pool);
return -1;
}
@@ -319,7 +319,7 @@ int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_in
*child_index = i;
/* supposed next slot is free */
- if (i + 1 >= scoreboard->nprocs) {
+ if (i + 1 >= (int)scoreboard->nprocs) {
scoreboard->free_proc = 0;
} else {
scoreboard->free_proc = i + 1;
diff --git a/sapi/fpm/fpm/fpm_signals.c b/sapi/fpm/fpm/fpm_signals.c
index e9f49d9e47..68cb15379b 100644
--- a/sapi/fpm/fpm/fpm_signals.c
+++ b/sapi/fpm/fpm/fpm_signals.c
@@ -242,9 +242,7 @@ int fpm_signals_init_child() /* {{{ */
return -1;
}
-#ifdef ZEND_SIGNALS
zend_signal_init();
-#endif
return 0;
}
/* }}} */
diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c
index 3e82face3c..42d7d7bae0 100644
--- a/sapi/fpm/fpm/fpm_status.c
+++ b/sapi/fpm/fpm/fpm_status.c
@@ -386,7 +386,8 @@ int fpm_status_handle_request(void) /* {{{ */
/* no need to test the var 'full' */
if (full_syntax) {
- int i, first;
+ unsigned int i;
+ int first;
zend_string *tmp_query_string;
char *query_string;
struct timeval duration, now;
diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in
index 9e5b593f83..56c18e862a 100644
--- a/sapi/fpm/php-fpm.conf.in
+++ b/sapi/fpm/php-fpm.conf.in
@@ -18,7 +18,7 @@
; Error log file
; If it's set to "syslog", log is sent to syslogd instead of being written
-; in a local file.
+; into a local file.
; Note: the default prefix is @EXPANDED_LOCALSTATEDIR@
; Default Value: log/php-fpm.log
;error_log = log/php-fpm.log
@@ -61,7 +61,7 @@
; Default Value: 0
;process_control_timeout = 0
-; The maximum number of processes FPM will fork. This has been design to control
+; The maximum number of processes FPM will fork. This has been designed to control
; the global number of processes when using dynamic PM within a lot of pools.
; Use it with caution.
; Note: A value of 0 indicates no limit
@@ -69,10 +69,10 @@
; process.max = 128
; Specify the nice(2) priority to apply to the master process (only if set)
-; The value can vary from -19 (highest priority) to 20 (lower priority)
+; The value can vary from -19 (highest priority) to 20 (lowest priority)
; Note: - It will only work if the FPM master process is launched as root
; - The pool process will inherit the master process priority
-; unless it specified otherwise
+; unless specified otherwise
; Default Value: no set
; process.priority = -19
@@ -99,8 +99,8 @@
; Default Value: not set (auto detection)
;events.mechanism = epoll
-; When FPM is build with systemd integration, specify the interval,
-; in second, between health report notification to systemd.
+; When FPM is built with systemd integration, specify the interval,
+; in seconds, between health report notification to systemd.
; Set to 0 to disable.
; Available Units: s(econds), m(inutes), h(ours)
; Default Unit: seconds
diff --git a/sapi/fpm/php-fpm.service.in b/sapi/fpm/php-fpm.service.in
index c135f04c27..a4d533c197 100644
--- a/sapi/fpm/php-fpm.service.in
+++ b/sapi/fpm/php-fpm.service.in
@@ -1,3 +1,7 @@
+# It's not recommended to modify this file in-place, because it
+# will be overwritten during upgrades. If you want to customize,
+# the best way is to use the "systemctl edit" command.
+
[Unit]
Description=The PHP FastCGI Process Manager
After=network.target
@@ -7,6 +11,7 @@ Type=@php_fpm_systemd@
PIDFile=@EXPANDED_LOCALSTATEDIR@/run/php-fpm.pid
ExecStart=@EXPANDED_SBINDIR@/php-fpm --nodaemonize --fpm-config @EXPANDED_SYSCONFDIR@/php-fpm.conf
ExecReload=/bin/kill -USR2 $MAINPID
+PrivateTmp=true
[Install]
WantedBy=multi-user.target
diff --git a/sapi/litespeed/lsapi_main.c b/sapi/litespeed/lsapi_main.c
index d5cefbc186..cd192ef0f0 100644
--- a/sapi/litespeed/lsapi_main.c
+++ b/sapi/litespeed/lsapi_main.c
@@ -370,7 +370,7 @@ static int sapi_lsapi_send_headers(sapi_headers_struct *sapi_headers)
/* {{{ sapi_lsapi_send_headers
*/
-static void sapi_lsapi_log_message(char *message)
+static void sapi_lsapi_log_message(char *message, int syslog_type_int)
{
char buf[8192];
int len = strlen( message );
@@ -443,15 +443,8 @@ static sapi_module_struct lsapi_sapi_module =
sapi_lsapi_register_variables, /* register server variables */
sapi_lsapi_log_message, /* Log message */
-
- NULL, /* php.ini path override */
- NULL, /* block interruptions */
- NULL, /* unblock interruptions */
- NULL, /* default post reader */
- NULL, /* treat data */
- NULL, /* executable location */
-
- 0, /* php.ini ignore */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
STANDARD_SAPI_MODULE_PROPERTIES
@@ -1241,9 +1234,7 @@ int main( int argc, char * argv[] )
tsrm_startup(1, 1, 0, NULL);
#endif
-#ifdef ZEND_SIGNALS
zend_signal_startup();
-#endif
if (argc > 1 ) {
if ( parse_opt( argc, argv, &climode,
@@ -1358,24 +1349,17 @@ int main( int argc, char * argv[] )
/* LiteSpeed PHP module starts here */
-#if PHP_MAJOR_VERSION > 4
-
/* {{{ arginfo */
ZEND_BEGIN_ARG_INFO(arginfo_litespeed__void, 0)
ZEND_END_ARG_INFO()
/* }}} */
-#else
-#define arginfo_litespeed__void NULL
-#endif
-
PHP_FUNCTION(litespeed_request_headers);
PHP_FUNCTION(litespeed_response_headers);
PHP_FUNCTION(apache_get_modules);
PHP_MINFO_FUNCTION(litespeed);
-
zend_function_entry litespeed_functions[] = {
PHP_FE(litespeed_request_headers, arginfo_litespeed__void)
PHP_FE(litespeed_response_headers, arginfo_litespeed__void)
diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c
index 682cc8ffba..fb81427c0f 100644
--- a/sapi/litespeed/lsapilib.c
+++ b/sapi/litespeed/lsapilib.c
@@ -1499,7 +1499,7 @@ int LSAPI_ReqBodyGetLine_r( LSAPI_Request * pReq, char * pBuf, size_t bufLen, in
char * pBufCur = pBuf;
char * pCur;
char * p;
- if (!pReq || (pReq->m_fd ==-1) ||( !pBuf )|| !getLF )
+ if (!pReq || (pReq->m_fd ==-1) ||( !pBuf )||(bufLen < 0 )|| !getLF )
return -1;
*getLF = 0;
while( (left = pBufEnd - pBufCur ) > 0 )
@@ -1543,7 +1543,7 @@ ssize_t LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, size_t bufLen )
ssize_t len;
off_t total;
/* char *pOldBuf = pBuf; */
- if (!pReq || (pReq->m_fd ==-1) || ( !pBuf ))
+ if (!pReq || (pReq->m_fd ==-1) || ( !pBuf )||(bufLen < 0 ))
return -1;
total = pReq->m_reqBodyLen - pReq->m_reqBodyRead;
diff --git a/sapi/phpdbg/phpdbg.c b/sapi/phpdbg/phpdbg.c
index 5fbc0d8126..c7ee88b616 100644
--- a/sapi/phpdbg/phpdbg.c
+++ b/sapi/phpdbg/phpdbg.c
@@ -18,10 +18,6 @@
+----------------------------------------------------------------------+
*/
-#if !defined(ZEND_SIGNALS) || defined(_WIN32)
-# include <signal.h>
-#endif
-
#include "phpdbg.h"
#include "phpdbg_prompt.h"
#include "phpdbg_bp.h"
@@ -80,6 +76,64 @@ PHP_INI_END()
static zend_bool phpdbg_booted = 0;
static zend_bool phpdbg_fully_started = 0;
+zend_bool use_mm_wrappers = 1;
+
+static void php_phpdbg_destroy_bp_file(zval *brake) /* {{{ */
+{
+ zend_hash_destroy(Z_ARRVAL_P(brake));
+ efree(Z_ARRVAL_P(brake));
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_symbol(zval *brake) /* {{{ */
+{
+ efree((char *) ((phpdbg_breaksymbol_t *) Z_PTR_P(brake))->symbol);
+ efree(Z_PTR_P(brake));
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_opcode(zval *brake) /* {{{ */
+{
+ efree((char *) ((phpdbg_breakop_t *) Z_PTR_P(brake))->name);
+ efree(Z_PTR_P(brake));
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_opline(zval *brake) /* {{{ */
+{
+ efree(Z_PTR_P(brake));
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_methods(zval *brake) /* {{{ */
+{
+ zend_hash_destroy(Z_ARRVAL_P(brake));
+ efree(Z_ARRVAL_P(brake));
+} /* }}} */
+
+static void php_phpdbg_destroy_bp_condition(zval *data) /* {{{ */
+{
+ phpdbg_breakcond_t *brake = (phpdbg_breakcond_t *) Z_PTR_P(data);
+
+ if (brake->ops) {
+ destroy_op_array(brake->ops);
+ efree(brake->ops);
+ }
+ efree((char*) brake->code);
+ efree(brake);
+} /* }}} */
+
+static void php_phpdbg_destroy_registered(zval *data) /* {{{ */
+{
+ zend_function *function = (zend_function *) Z_PTR_P(data);
+ destroy_zend_function(function);
+} /* }}} */
+
+static void php_phpdbg_destroy_file_source(zval *data) /* {{{ */
+{
+ phpdbg_file_source *source = (phpdbg_file_source *) Z_PTR_P(data);
+ destroy_op_array(&source->op_array);
+ if (source->buf) {
+ efree(source->buf);
+ }
+ efree(source);
+} /* }}} */
static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
{
@@ -132,75 +186,6 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
{
- ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL);
- REGISTER_INI_ENTRIES();
-
- zend_execute_ex = phpdbg_execute_ex;
-
- REGISTER_STRINGL_CONSTANT("PHPDBG_VERSION", PHPDBG_VERSION, sizeof(PHPDBG_VERSION)-1, CONST_CS|CONST_PERSISTENT);
-
- REGISTER_LONG_CONSTANT("PHPDBG_FILE", FILE_PARAM, CONST_CS|CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("PHPDBG_METHOD", METHOD_PARAM, CONST_CS|CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("PHPDBG_LINENO", NUMERIC_PARAM, CONST_CS|CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("PHPDBG_FUNC", STR_PARAM, CONST_CS|CONST_PERSISTENT);
-
- REGISTER_LONG_CONSTANT("PHPDBG_COLOR_PROMPT", PHPDBG_COLOR_PROMPT, CONST_CS|CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("PHPDBG_COLOR_NOTICE", PHPDBG_COLOR_NOTICE, CONST_CS|CONST_PERSISTENT);
- REGISTER_LONG_CONSTANT("PHPDBG_COLOR_ERROR", PHPDBG_COLOR_ERROR, CONST_CS|CONST_PERSISTENT);
-
- return SUCCESS;
-} /* }}} */
-
-static void php_phpdbg_destroy_bp_file(zval *brake) /* {{{ */
-{
- zend_hash_destroy(Z_ARRVAL_P(brake));
- efree(Z_ARRVAL_P(brake));
-} /* }}} */
-
-static void php_phpdbg_destroy_bp_symbol(zval *brake) /* {{{ */
-{
- efree((char *) ((phpdbg_breaksymbol_t *) Z_PTR_P(brake))->symbol);
- efree(Z_PTR_P(brake));
-} /* }}} */
-
-static void php_phpdbg_destroy_bp_opcode(zval *brake) /* {{{ */
-{
- efree((char *) ((phpdbg_breakop_t *) Z_PTR_P(brake))->name);
- efree(Z_PTR_P(brake));
-} /* }}} */
-
-static void php_phpdbg_destroy_bp_opline(zval *brake) /* {{{ */
-{
- efree(Z_PTR_P(brake));
-} /* }}} */
-
-static void php_phpdbg_destroy_bp_methods(zval *brake) /* {{{ */
-{
- zend_hash_destroy(Z_ARRVAL_P(brake));
- efree(Z_ARRVAL_P(brake));
-} /* }}} */
-
-static void php_phpdbg_destroy_bp_condition(zval *data) /* {{{ */
-{
- phpdbg_breakcond_t *brake = (phpdbg_breakcond_t *) Z_PTR_P(data);
-
- if (brake->ops) {
- destroy_op_array(brake->ops);
- efree(brake->ops);
- }
- efree((char*) brake->code);
- efree(brake);
-} /* }}} */
-
-static void php_phpdbg_destroy_registered(zval *data) /* {{{ */
-{
- zend_function *function = (zend_function *) Z_PTR_P(data);
- destroy_zend_function(function);
-} /* }}} */
-
-
-static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
-{
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE], 8, NULL, php_phpdbg_destroy_bp_file, 0);
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING], 8, NULL, php_phpdbg_destroy_bp_file, 0);
zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
@@ -216,10 +201,28 @@ static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0);
zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0);
+ zend_hash_init(&PHPDBG_G(file_sources), 0, NULL, php_phpdbg_destroy_file_source, 0);
+ phpdbg_setup_watchpoints();
+
+ REGISTER_INI_ENTRIES();
+
+ zend_execute_ex = phpdbg_execute_ex;
+
+ REGISTER_STRINGL_CONSTANT("PHPDBG_VERSION", PHPDBG_VERSION, sizeof(PHPDBG_VERSION)-1, CONST_CS|CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("PHPDBG_FILE", FILE_PARAM, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_METHOD", METHOD_PARAM, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_LINENO", NUMERIC_PARAM, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_FUNC", STR_PARAM, CONST_CS|CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("PHPDBG_COLOR_PROMPT", PHPDBG_COLOR_PROMPT, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_COLOR_NOTICE", PHPDBG_COLOR_NOTICE, CONST_CS|CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("PHPDBG_COLOR_ERROR", PHPDBG_COLOR_ERROR, CONST_CS|CONST_PERSISTENT);
+
return SUCCESS;
} /* }}} */
-static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
+static PHP_MSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
{
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE_PENDING]);
@@ -232,11 +235,19 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_MAP]);
- zend_hash_destroy(&PHPDBG_G(file_sources));
zend_hash_destroy(&PHPDBG_G(seek));
zend_hash_destroy(&PHPDBG_G(registered));
- zend_hash_destroy(&PHPDBG_G(watchpoints));
- zend_llist_destroy(&PHPDBG_G(watchlist_mem));
+ phpdbg_destroy_watchpoints();
+
+ if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ phpdbg_notice("stop", "type=\"normal\"", "Script ended normally");
+ }
+
+ /* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */
+ if (use_mm_wrappers) {
+ /* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */
+ *(int *) zend_mm_get_heap() = 0;
+ }
if (PHPDBG_G(buffer)) {
free(PHPDBG_G(buffer));
@@ -271,6 +282,26 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
PHPDBG_G(oplog_list) = NULL;
}
+ fflush(stdout);
+ if (SG(request_info).argv0) {
+ free(SG(request_info).argv0);
+ SG(request_info).argv0 = NULL;
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
+{
+ /* deactivate symbol table caching to have these properly destroyed upon stack leaving (especially important for watchpoints) */
+ EG(symtable_cache_limit) = EG(symtable_cache) - 1;
+
+ return SUCCESS;
+} /* }}} */
+
+static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
+{
if (PHPDBG_G(stdin_file)) {
fclose(PHPDBG_G(stdin_file));
PHPDBG_G(stdin_file) = NULL;
@@ -776,7 +807,7 @@ static zend_module_entry sapi_phpdbg_module_entry = {
PHPDBG_NAME,
phpdbg_user_functions,
PHP_MINIT(phpdbg),
- NULL,
+ PHP_MSHUTDOWN(phpdbg),
PHP_RINIT(phpdbg),
PHP_RSHUTDOWN(phpdbg),
NULL,
@@ -784,13 +815,17 @@ static zend_module_entry sapi_phpdbg_module_entry = {
STANDARD_MODULE_PROPERTIES
};
+static void phpdbg_interned_strings_nothing(void) { }
+
static inline int php_sapi_phpdbg_module_startup(sapi_module_struct *module) /* {{{ */
{
if (php_module_startup(module, &sapi_phpdbg_module_entry, 1) == FAILURE) {
return FAILURE;
}
+ /* prevent zend_interned_strings_restore from invalidating our string pointers too early (in phpdbg allocated memory only gets freed after module shutdown) */
+ zend_interned_strings_restore = phpdbg_interned_strings_nothing;
- phpdbg_booted=1;
+ phpdbg_booted = 1;
return SUCCESS;
} /* }}} */
@@ -819,7 +854,7 @@ static void php_sapi_phpdbg_send_header(sapi_header_struct *sapi_header, void *s
}
/* }}} */
-static void php_sapi_phpdbg_log_message(char *message) /* {{{ */
+static void php_sapi_phpdbg_log_message(char *message, int syslog_type_int) /* {{{ */
{
/*
* We must not request TSRM before being booted
@@ -869,17 +904,15 @@ static void php_sapi_phpdbg_log_message(char *message) /* {{{ */
}
/* }}} */
-static int php_sapi_phpdbg_deactivate(void) /* {{{ */
+static int php_sapi_phpdbg_activate(void) /* {{{ */
{
- fflush(stdout);
- if (SG(request_info).argv0) {
- free(SG(request_info).argv0);
- SG(request_info).argv0 = NULL;
- }
+ return SUCCESS;
+}
+static int php_sapi_phpdbg_deactivate(void) /* {{{ */
+{
return SUCCESS;
}
-/* }}} */
static void php_sapi_phpdbg_register_vars(zval *track_vars_array) /* {{{ */
{
@@ -1021,7 +1054,7 @@ static sapi_module_struct phpdbg_sapi_module = {
php_sapi_phpdbg_module_startup, /* startup */
php_module_shutdown_wrapper, /* shutdown */
- NULL, /* activate */
+ php_sapi_phpdbg_activate, /* activate */
php_sapi_phpdbg_deactivate, /* deactivate */
php_sapi_phpdbg_ub_write, /* unbuffered write */
@@ -1268,16 +1301,12 @@ void phpdbg_signal_handler(int sig, siginfo_t *info, void *context) /* {{{ */
switch (sig) {
case SIGBUS:
case SIGSEGV:
- if (PHPDBG_G(sigsegv_bailout)) {
- LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
- }
is_handled = phpdbg_watchpoint_segfault_handler(info, context);
if (is_handled == FAILURE) {
-#ifdef ZEND_SIGNALS
+ if (PHPDBG_G(sigsegv_bailout)) {
+ LONGJMP(*PHPDBG_G(sigsegv_bailout), FAILURE);
+ }
zend_sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
-#else
- sigaction(sig, &PHPDBG_G(old_sigsegv_signal), NULL);
-#endif
}
break;
}
@@ -1366,12 +1395,15 @@ int main(int argc, char **argv) /* {{{ */
FILE* stream = NULL;
char *print_opline_func;
zend_bool ext_stmt = 0;
- zend_bool use_mm_wrappers = 0;
zend_bool is_exit;
int exit_status;
char *read_from_stdin = NULL;
zend_string *backup_phpdbg_compile = NULL;
zend_bool show_help = 0, show_version = 0;
+ void* (*_malloc)(size_t);
+ void (*_free)(void*);
+ void* (*_realloc)(void*, size_t);
+
#ifndef _WIN32
struct sigaction sigio_struct;
@@ -1393,17 +1425,15 @@ int main(int argc, char **argv) /* {{{ */
setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
#endif
+phpdbg_main:
#ifdef ZTS
tsrm_startup(1, 1, 0, NULL);
(void)ts_resource(0);
ZEND_TSRMLS_CACHE_UPDATE();
#endif
-#ifdef ZEND_SIGNALS
zend_signal_startup();
-#endif
-phpdbg_main:
ini_entries = NULL;
ini_entries_len = 0;
ini_ignore = 0;
@@ -1489,9 +1519,6 @@ phpdbg_main:
} break;
case 'S': { /* set SAPI name */
- if (sapi_name) {
- free(sapi_name);
- }
sapi_name = strdup(php_optarg);
} break;
@@ -1636,26 +1663,34 @@ phpdbg_main:
phpdbg->ini_entries = ini_entries;
+ ZEND_INIT_MODULE_GLOBALS(phpdbg, php_phpdbg_globals_ctor, NULL);
+
+ /* set default colors */
+ phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold"));
+ phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold"));
+ phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green"));
+
+ /* set default prompt */
+ phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT);
+
+ if (settings > (zend_phpdbg_globals *) 0x2) {
+#ifdef ZTS
+ *((zend_phpdbg_globals *) (*((void ***) TSRMLS_CACHE))[TSRM_UNSHUFFLE_RSRC_ID(phpdbg_globals_id)]) = *settings;
+#else
+ phpdbg_globals = *settings;
+#endif
+ free(settings);
+ }
+
+ /* set flags from command line */
+ PHPDBG_G(flags) = flags;
+
if (phpdbg->startup(phpdbg) == SUCCESS) {
zend_mm_heap *mm_heap;
#ifdef _WIN32
EXCEPTION_POINTERS *xp;
__try {
#endif
- void* (*_malloc)(size_t);
- void (*_free)(void*);
- void* (*_realloc)(void*, size_t);
-
- /* set flags from command line */
- PHPDBG_G(flags) = flags;
-
- /* set default colors */
- phpdbg_set_color_ex(PHPDBG_COLOR_PROMPT, PHPDBG_STRL("white-bold"));
- phpdbg_set_color_ex(PHPDBG_COLOR_ERROR, PHPDBG_STRL("red-bold"));
- phpdbg_set_color_ex(PHPDBG_COLOR_NOTICE, PHPDBG_STRL("green"));
-
- /* set default prompt */
- phpdbg_set_prompt(PHPDBG_DEFAULT_PROMPT);
if (show_version || show_help) {
/* It ain't gonna proceed to real execution anyway,
@@ -1694,14 +1729,9 @@ phpdbg_main:
goto free_and_return;
}
- if (settings > (zend_phpdbg_globals *) 0x2) {
-#ifdef ZTS
- *((zend_phpdbg_globals *) (*((void ***) TSRMLS_CACHE))[TSRM_UNSHUFFLE_RSRC_ID(phpdbg_globals_id)]) = *settings;
-#else
- phpdbg_globals = *settings;
-#endif
- free(settings);
- }
+ zend_try {
+ zend_signal_activate();
+ } zend_end_try();
/* setup remote server if necessary */
if (cleaning <= 0 && listen > 0) {
@@ -1711,7 +1741,7 @@ phpdbg_main:
}
#ifndef _WIN32
- sigaction(SIGIO, &sigio_struct, NULL);
+ zend_sigaction(SIGIO, &sigio_struct, NULL);
#endif
/* set remote flag to stop service shutting down upon quit */
@@ -1719,7 +1749,7 @@ phpdbg_main:
#ifndef _WIN32
} else {
- signal(SIGHUP, phpdbg_sighup_handler);
+ zend_signal(SIGHUP, phpdbg_sighup_handler);
#endif
}
@@ -1728,8 +1758,6 @@ phpdbg_main:
use_mm_wrappers = !_malloc && !_realloc && !_free;
- phpdbg_init_list();
-
PHPDBG_G(original_free_function) = _free;
_free = phpdbg_watch_efree;
@@ -1743,21 +1771,10 @@ phpdbg_main:
zend_mm_set_custom_handlers(mm_heap, _malloc, _free, _realloc);
}
- phpdbg_setup_watchpoints();
+ _free = PHPDBG_G(original_free_function);
-#if defined(ZEND_SIGNALS) && !defined(_WIN32)
- zend_try {
- zend_signal_activate();
- } zend_end_try();
-#endif
-#if defined(ZEND_SIGNALS) && !defined(_WIN32)
- zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
- zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
-#elif !defined(_WIN32)
- sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
- sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal));
-#endif
+ phpdbg_init_list();
PHPDBG_G(sapi_name_ptr) = sapi_name;
@@ -1795,24 +1812,24 @@ phpdbg_main:
return 1;
}
+#ifndef _WIN32
+ zend_try { zend_sigaction(SIGSEGV, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
+ zend_try { zend_sigaction(SIGBUS, &signal_struct, &PHPDBG_G(old_sigsegv_signal)); } zend_end_try();
+#endif
+
/* do not install sigint handlers for remote consoles */
/* sending SIGINT then provides a decent way of shutting down the server */
#ifndef _WIN32
if (listen < 0) {
#endif
-#if defined(ZEND_SIGNALS) && !defined(_WIN32)
zend_try { zend_signal(SIGINT, phpdbg_sigint_handler); } zend_end_try();
-#else
- signal(SIGINT, phpdbg_sigint_handler);
-#endif
#ifndef _WIN32
}
/* setup io here */
if (remote) {
PHPDBG_G(flags) |= PHPDBG_IS_REMOTE;
-
- signal(SIGPIPE, SIG_IGN);
+ zend_signal(SIGPIPE, SIG_IGN);
}
PHPDBG_G(io)[PHPDBG_STDIN].ptr = stdin;
PHPDBG_G(io)[PHPDBG_STDIN].fd = fileno(stdin);
@@ -2076,6 +2093,10 @@ phpdbg_out:
zend_objects_store_mark_destructed(&EG(objects_store));
}
+ zend_try {
+ php_request_shutdown(NULL);
+ } zend_end_try();
+
if (PHPDBG_G(exec) && !memcmp("-", PHPDBG_G(exec), 2)) { /* i.e. execution context has been read from stdin - back it up */
phpdbg_file_source *data = zend_hash_str_find_ptr(&PHPDBG_G(file_sources), PHPDBG_G(exec), PHPDBG_G(exec_len));
backup_phpdbg_compile = zend_string_alloc(data->len + 2, 1);
@@ -2113,44 +2134,47 @@ phpdbg_out:
}
}
- /* hack to restore mm_heap->use_custom_heap in order to receive memory leak info */
- if (use_mm_wrappers) {
- /* ASSUMING that mm_heap->use_custom_heap is the first element of the struct ... */
- *(int *) mm_heap = 0;
- }
- zend_try {
- php_request_shutdown(NULL);
- } zend_end_try();
-
if (exit_status == 0) {
exit_status = EG(exit_status);
}
+ php_output_deactivate();
+
if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+ PHPDBG_G(flags) |= PHPDBG_IS_QUITTING;
if (PHPDBG_G(in_execution) || is_exit) {
if (!quit_immediately && !phpdbg_startup_run) {
- phpdbg_notice("stop", "type=\"normal\"", "Script ended normally");
+ PHPDBG_G(flags) -= PHPDBG_IS_QUITTING;
cleaning++;
}
}
}
- php_output_deactivate();
{
php_stream_wrapper *wrapper = zend_hash_str_find_ptr(php_stream_get_url_stream_wrappers_hash(), ZEND_STRL("php"));
wrapper->wops->stream_opener = PHPDBG_G(orig_url_wrap_php);
}
+ zend_hash_destroy(&PHPDBG_G(file_sources));
+
zend_try {
php_module_shutdown();
} zend_end_try();
#ifndef _WIN32
+ /* force override (no zend_signals) to prevent crashes due to signal recursion in SIGSEGV/SIGBUS handlers */
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGBUS, SIG_DFL);
+
/* reset it... else we risk a stack overflow upon next run (when clean'ing) */
php_stream_stdio_ops.write = PHPDBG_G(php_stdiop_write);
#endif
+ }
- sapi_shutdown();
+ sapi_shutdown();
+
+ if (sapi_name) {
+ free(sapi_name);
}
free_and_return:
@@ -2159,6 +2183,17 @@ free_and_return:
read_from_stdin = NULL;
}
+#ifdef ZTS
+ /* reset to original handlers - otherwise PHPDBG_G() in phpdbg_watch_efree will be segfaulty (with e.g. USE_ZEND_ALLOC=0) */
+ if (!use_mm_wrappers) {
+ zend_mm_set_custom_handlers(zend_mm_get_heap(), _malloc, _free, _realloc);
+ }
+
+ ts_free_id(phpdbg_globals_id);
+
+ tsrm_shutdown();
+#endif
+
if ((cleaning > 0 || remote) && !quit_immediately) {
/* reset internal php_getopt state */
php_getopt(-1, argv, OPTIONS, NULL, &php_optind, 0, 0);
@@ -2166,11 +2201,6 @@ free_and_return:
goto phpdbg_main;
}
-#ifdef ZTS
- /* bugggy */
- /* tsrm_shutdown(); */
-#endif
-
if (backup_phpdbg_compile) {
zend_string_free(backup_phpdbg_compile);
}
@@ -2181,10 +2211,6 @@ free_and_return:
}
#endif
- if (PHPDBG_G(sapi_name_ptr)) {
- free(PHPDBG_G(sapi_name_ptr));
- }
-
/* usually 0; just for -rr */
return exit_status;
} /* }}} */
diff --git a/sapi/phpdbg/phpdbg.h b/sapi/phpdbg/phpdbg.h
index a0d3e9eee9..ef3b017b6d 100644
--- a/sapi/phpdbg/phpdbg.h
+++ b/sapi/phpdbg/phpdbg.h
@@ -45,8 +45,11 @@
#include "zend_globals.h"
#include "zend_ini_scanner.h"
#include "zend_stream.h"
-#ifndef _WIN32
-# include "zend_signal.h"
+#include "zend_signal.h"
+#if !defined(_WIN32) && !defined(ZEND_SIGNALS) && defined(HAVE_SIGNAL_H)
+# include <signal.h>
+#elif defined(PHP_WIN32)
+# include "win32/signal.h"
#endif
#include "SAPI.h"
#include <fcntl.h>
@@ -251,12 +254,15 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
#endif
phpdbg_btree watchpoint_tree; /* tree with watchpoints */
phpdbg_btree watch_HashTables; /* tree with original dtors of watchpoints */
- HashTable watchpoints; /* watchpoints */
+ HashTable watch_elements; /* user defined watch elements */
HashTable watch_collisions; /* collision table to check if multiple watches share the same recursive watchpoint */
- zend_llist watchlist_mem; /* triggered watchpoints */
+ HashTable watch_recreation; /* watch elements pending recreation of their respective watchpoints */
+ HashTable watch_free; /* pointers to watch for being freed */
+ HashTable *watchlist_mem; /* triggered watchpoints */
+ HashTable *watchlist_mem_backup; /* triggered watchpoints backup table while iterating over it */
zend_bool watchpoint_hit; /* a watchpoint was hit */
void (*original_free_function)(void *); /* the original AG(mm_heap)->_free function */
- phpdbg_watchpoint_t *watch_tmp; /* temporary pointer for a watchpoint */
+ phpdbg_watch_element *watch_tmp; /* temporary pointer for a watch element */
char *exec; /* file to execute */
size_t exec_len; /* size of exec */
diff --git a/sapi/phpdbg/phpdbg_btree.c b/sapi/phpdbg/phpdbg_btree.c
index 977fb1606c..49b8cc6fa2 100644
--- a/sapi/phpdbg/phpdbg_btree.c
+++ b/sapi/phpdbg/phpdbg_btree.c
@@ -25,14 +25,17 @@
branch = branch->branches[!!(n)];
#ifdef _Win32
-# define emalloc malloc
-# define efree free
+# undef pemalloc
+# undef pefree
+# define pemalloc(size, persistent) malloc(size)
+# define pefree(ptr, persistent) free(ptr)
#endif
/* depth in bits */
void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth) {
tree->depth = depth;
tree->branch = NULL;
+ tree->persistent = 0;
tree->count = 0;
}
@@ -157,7 +160,7 @@ int phpdbg_btree_insert_or_update(phpdbg_btree *tree, zend_ulong idx, void *ptr,
}
{
- phpdbg_btree_branch *memory = *branch = emalloc((i + 2) * sizeof(phpdbg_btree_branch));
+ phpdbg_btree_branch *memory = *branch = pemalloc((i + 2) * sizeof(phpdbg_btree_branch), tree->persistent);
do {
(*branch)->branches[!((idx >> i) % 2)] = NULL;
branch = &(*branch)->branches[(idx >> i) % 2];
@@ -199,14 +202,14 @@ check_branch_existence:
tree->count--;
if (i_last_dual_branch == -1) {
- efree(tree->branch);
+ pefree(tree->branch, tree->persistent);
tree->branch = NULL;
} else {
if (last_dual_branch->branches[last_dual_branch_branch] == last_dual_branch + 1) {
phpdbg_btree_branch *original_branch = last_dual_branch->branches[!last_dual_branch_branch];
memcpy(last_dual_branch + 1, last_dual_branch->branches[!last_dual_branch_branch], (i_last_dual_branch + 1) * sizeof(phpdbg_btree_branch));
- efree(last_dual_branch->branches[!last_dual_branch_branch]);
+ pefree(last_dual_branch->branches[!last_dual_branch_branch], tree->persistent);
last_dual_branch->branches[!last_dual_branch_branch] = last_dual_branch + 1;
branch = last_dual_branch->branches[!last_dual_branch_branch];
@@ -214,7 +217,7 @@ check_branch_existence:
branch = (branch->branches[branch->branches[1] == ++original_branch] = last_dual_branch + i_last_dual_branch - i + 1);
}
} else {
- efree(last_dual_branch->branches[last_dual_branch_branch]);
+ pefree(last_dual_branch->branches[last_dual_branch_branch], tree->persistent);
}
last_dual_branch->branches[last_dual_branch_branch] = NULL;
@@ -223,6 +226,26 @@ check_branch_existence:
return SUCCESS;
}
+void phpdbg_btree_clean_recursive(phpdbg_btree_branch *branch, zend_ulong depth, zend_bool persistent) {
+ phpdbg_btree_branch *start = branch;
+ while (depth--) {
+ zend_bool use_branch = branch + 1 == branch->branches[0];
+ if (branch->branches[use_branch]) {
+ phpdbg_btree_clean_recursive(branch->branches[use_branch], depth, persistent);
+ }
+ }
+
+ pefree(start, persistent);
+}
+
+void phpdbg_btree_clean(phpdbg_btree *tree) {
+ if (tree->branch) {
+ phpdbg_btree_clean_recursive(tree->branch, tree->depth, tree->persistent);
+ tree->branch = NULL;
+ tree->count = 0;
+ }
+}
+
void phpdbg_btree_branch_dump(phpdbg_btree_branch *branch, zend_ulong depth) {
if (branch) {
if (depth--) {
diff --git a/sapi/phpdbg/phpdbg_btree.h b/sapi/phpdbg/phpdbg_btree.h
index 8fe908ddb5..a756af8d59 100644
--- a/sapi/phpdbg/phpdbg_btree.h
+++ b/sapi/phpdbg/phpdbg_btree.h
@@ -37,6 +37,7 @@ union _phpdbg_btree_branch {
typedef struct {
zend_ulong count;
zend_ulong depth;
+ zend_bool persistent;
phpdbg_btree_branch *branch;
} phpdbg_btree;
@@ -47,6 +48,7 @@ typedef struct {
} phpdbg_btree_position;
void phpdbg_btree_init(phpdbg_btree *tree, zend_ulong depth);
+void phpdbg_btree_clean(phpdbg_btree *tree);
phpdbg_btree_result *phpdbg_btree_find(phpdbg_btree *tree, zend_ulong idx);
phpdbg_btree_result *phpdbg_btree_find_closest(phpdbg_btree *tree, zend_ulong idx);
phpdbg_btree_position phpdbg_btree_find_between(phpdbg_btree *tree, zend_ulong lower_idx, zend_ulong higher_idx);
diff --git a/sapi/phpdbg/phpdbg_cmd.h b/sapi/phpdbg/phpdbg_cmd.h
index 7022d61fcc..b872e0a8a9 100644
--- a/sapi/phpdbg/phpdbg_cmd.h
+++ b/sapi/phpdbg/phpdbg_cmd.h
@@ -22,6 +22,7 @@
#define PHPDBG_CMD_H
#include "TSRM.h"
+#include "zend_generators.h"
/* {{{ Command and Parameter */
enum {
@@ -113,6 +114,7 @@ struct _phpdbg_command_t {
typedef struct {
int num;
+ zend_generator *generator;
zend_execute_data *execute_data;
} phpdbg_frame_t;
/* }}} */
diff --git a/sapi/phpdbg/phpdbg_frame.c b/sapi/phpdbg/phpdbg_frame.c
index 3b4bed5182..36832c0417 100644
--- a/sapi/phpdbg/phpdbg_frame.c
+++ b/sapi/phpdbg/phpdbg_frame.c
@@ -23,26 +23,104 @@
#include "phpdbg_utils.h"
#include "phpdbg_frame.h"
#include "phpdbg_list.h"
+#include "zend_smart_str.h"
ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
+static inline void phpdbg_append_individual_arg(smart_str *s, uint32_t i, zend_function *func, zval *arg) {
+ const zend_arg_info *arginfo = func->common.arg_info;
+ char *arg_name = NULL;
+
+ if (i) {
+ smart_str_appends(s, ", ");
+ }
+ if (i < func->common.num_args) {
+ if (arginfo) {
+ if (func->type == ZEND_INTERNAL_FUNCTION) {
+ arg_name = (char *) ((zend_internal_arg_info *) &arginfo[i])->name;
+ } else {
+ arg_name = ZSTR_VAL(arginfo[i].name);
+ }
+ }
+ smart_str_appends(s, arg_name ? arg_name : "?");
+ smart_str_appendc(s, '=');
+ }
+ {
+ char *arg_print = phpdbg_short_zval_print(arg, 40);
+ smart_str_appends(s, arg_print);
+ efree(arg_print);
+ }
+}
+
+zend_string *phpdbg_compile_stackframe(zend_execute_data *ex) {
+ smart_str s = {0};
+ zend_op_array *op_array = &ex->func->op_array;
+ uint32_t i = 0, first_extra_arg = op_array->num_args, num_args = ZEND_CALL_NUM_ARGS(ex);
+ zval *p = ZEND_CALL_ARG(ex, 1);
+
+ if (op_array->scope) {
+ smart_str_append(&s, op_array->scope->name);
+ smart_str_appends(&s, "::");
+ }
+ smart_str_append(&s, op_array->function_name);
+ smart_str_appendc(&s, '(');
+ if (ZEND_CALL_NUM_ARGS(ex) > first_extra_arg) {
+ while (i < first_extra_arg) {
+ phpdbg_append_individual_arg(&s, i, ex->func, p);
+ p++;
+ i++;
+ }
+ p = ZEND_CALL_VAR_NUM(ex, op_array->last_var + op_array->T);
+ }
+ while (i < num_args) {
+ phpdbg_append_individual_arg(&s, i, ex->func, p);
+ p++;
+ i++;
+ }
+ smart_str_appendc(&s, ')');
+
+ if (ex->func->type == ZEND_USER_FUNCTION) {
+ smart_str_appends(&s, " at ");
+ smart_str_append(&s, op_array->filename);
+ smart_str_appendc(&s, ':');
+ smart_str_append_unsigned(&s, ex->opline->lineno);
+ } else {
+ smart_str_appends(&s, " [internal function]");
+ }
+
+ return s.s;
+}
+
+void phpdbg_print_cur_frame_info() {
+ const char *file_chr = zend_get_executed_filename();
+ zend_string *file = zend_string_init(file_chr, strlen(file_chr), 0);
+
+ phpdbg_list_file(file, 3, zend_get_executed_lineno() - 1, zend_get_executed_lineno());
+ efree(file);
+}
+
void phpdbg_restore_frame(void) /* {{{ */
{
if (PHPDBG_FRAME(num) == 0) {
return;
}
+ if (PHPDBG_FRAME(generator)) {
+ if (PHPDBG_FRAME(generator)->execute_data->call) {
+ PHPDBG_FRAME(generator)->frozen_call_stack = zend_generator_freeze_call_stack(PHPDBG_FRAME(generator)->execute_data);
+ }
+ PHPDBG_FRAME(generator) = NULL;
+ }
+
PHPDBG_FRAME(num) = 0;
/* move things back */
EG(current_execute_data) = PHPDBG_FRAME(execute_data);
-
- EG(scope) = PHPDBG_EX(func)->op_array.scope;
} /* }}} */
void phpdbg_switch_frame(int frame) /* {{{ */
{
- zend_execute_data *execute_data = PHPDBG_FRAME(num)?PHPDBG_FRAME(execute_data):EG(current_execute_data);
+ zend_execute_data *execute_data = PHPDBG_FRAME(num) ? PHPDBG_FRAME(execute_data) : EG(current_execute_data);
int i = 0;
if (PHPDBG_FRAME(num) == frame) {
@@ -78,18 +156,17 @@ void phpdbg_switch_frame(int frame) /* {{{ */
/* backup things and jump back */
PHPDBG_FRAME(execute_data) = EG(current_execute_data);
EG(current_execute_data) = execute_data;
-
- EG(scope) = PHPDBG_EX(func)->op_array.scope;
}
- phpdbg_notice("frame", "id=\"%d\"", "Switched to frame #%d", frame);
+ phpdbg_try_access {
+ zend_string *s = phpdbg_compile_stackframe(EG(current_execute_data));
+ phpdbg_notice("frame", "id=\"%d\" frameinfo=\"%.*s\"", "Switched to frame #%d: %.*s", frame, (int) ZSTR_LEN(s), ZSTR_VAL(s));
+ zend_string_release(s);
+ } phpdbg_catch_access {
+ phpdbg_notice("frame", "id=\"%d\"", "Switched to frame #%d", frame);
+ } phpdbg_end_try_access();
- {
- const char *file_chr = zend_get_executed_filename();
- zend_string *file = zend_string_init(file_chr, strlen(file_chr), 0);
- phpdbg_list_file(file, 3, zend_get_executed_lineno() - 1, zend_get_executed_lineno());
- efree(file);
- }
+ phpdbg_print_cur_frame_info();
} /* }}} */
static void phpdbg_dump_prototype(zval *tmp) /* {{{ */
@@ -242,3 +319,27 @@ void phpdbg_dump_backtrace(size_t num) /* {{{ */
PHPDBG_OUTPUT_BACKUP_RESTORE();
} /* }}} */
+
+void phpdbg_open_generator_frame(zend_generator *gen) {
+ zend_string *s;
+
+ if (EG(current_execute_data) == gen->execute_data) {
+ return;
+ }
+
+ phpdbg_restore_frame();
+
+ PHPDBG_FRAME(num) = -1;
+ PHPDBG_FRAME(generator) = gen;
+
+ EG(current_execute_data) = gen->execute_data;
+ if (gen->frozen_call_stack) {
+ zend_generator_restore_call_stack(gen);
+ }
+ gen->execute_data->prev_execute_data = NULL;
+
+ s = phpdbg_compile_stackframe(EG(current_execute_data));
+ phpdbg_notice("frame", "handle=\"%d\" frameinfo=\"%.*s\"", "Switched to generator with handle #%d: %.*s", gen->std.handle, (int) ZSTR_LEN(s), ZSTR_VAL(s));
+ zend_string_release(s);
+ phpdbg_print_cur_frame_info();
+}
diff --git a/sapi/phpdbg/phpdbg_frame.h b/sapi/phpdbg/phpdbg_frame.h
index 82fe24ef34..28f48c8de1 100644
--- a/sapi/phpdbg/phpdbg_frame.h
+++ b/sapi/phpdbg/phpdbg_frame.h
@@ -23,8 +23,10 @@
#include "TSRM.h"
+zend_string *phpdbg_compile_stackframe(zend_execute_data *);
void phpdbg_restore_frame(void);
void phpdbg_switch_frame(int);
void phpdbg_dump_backtrace(size_t);
+void phpdbg_open_generator_frame(zend_generator *);
#endif /* PHPDBG_FRAME_H */
diff --git a/sapi/phpdbg/phpdbg_help.c b/sapi/phpdbg/phpdbg_help.c
index c57dd5cc6e..817690322f 100644
--- a/sapi/phpdbg/phpdbg_help.c
+++ b/sapi/phpdbg/phpdbg_help.c
@@ -349,35 +349,36 @@ phpdbg_help_text_t phpdbg_help_text[] = {
"It supports the following commands:" CR CR
"**Information**" CR
-" **list** list PHP source" CR
-" **info** displays information on the debug session" CR
-" **print** show opcodes" CR
-" **frame** select a stack frame and print a stack frame summary" CR
-" **back** shows the current backtrace" CR
-" **help** provide help on a topic" CR CR
+" **list** list PHP source" CR
+" **info** displays information on the debug session" CR
+" **print** show opcodes" CR
+" **frame** select a stack frame and print a stack frame summary" CR
+" **generator** show active generators or select a generator frame" CR
+" **back** shows the current backtrace" CR
+" **help** provide help on a topic" CR CR
"**Starting and Stopping Execution**" CR
-" **exec** set execution context" CR
-" **stdin** set executing script from stdin" CR
-" **run** attempt execution" CR
-" **step** continue execution until other line is reached" CR
-" **continue** continue execution" CR
-" **until** continue execution up to the given location" CR
-" **next** continue execution up to the given location and halt on the first line after it" CR
-" **finish** continue up to end of the current execution frame" CR
-" **leave** continue up to end of the current execution frame and halt after the calling instruction" CR
-" **break** set a breakpoint at the specified target" CR
-" **watch** set a watchpoint on $variable" CR
-" **clear** clear one or all breakpoints" CR
-" **clean** clean the execution environment" CR CR
+" **exec** set execution context" CR
+" **stdin** set executing script from stdin" CR
+" **run** attempt execution" CR
+" **step** continue execution until other line is reached" CR
+" **continue** continue execution" CR
+" **until** continue execution up to the given location" CR
+" **next** continue execution up to the given location and halt on the first line after it" CR
+" **finish** continue up to end of the current execution frame" CR
+" **leave** continue up to end of the current execution frame and halt after the calling instruction" CR
+" **break** set a breakpoint at the specified target" CR
+" **watch** set a watchpoint on $variable" CR
+" **clear** clear one or all breakpoints" CR
+" **clean** clean the execution environment" CR CR
"**Miscellaneous**" CR
-" **set** set the phpdbg configuration" CR
-" **source** execute a phpdbginit script" CR
-" **register** register a phpdbginit function as a command alias" CR
-" **sh** shell a command" CR
-" **ev** evaluate some code" CR
-" **quit** exit phpdbg" CR CR
+" **set** set the phpdbg configuration" CR
+" **source** execute a phpdbginit script" CR
+" **register** register a phpdbginit function as a command alias" CR
+" **sh** shell a command" CR
+" **ev** evaluate some code" CR
+" **quit** exit phpdbg" CR CR
"Type **help <command>** or (**help alias**) to get detailed help on any of the above commands, "
"for example **help list** or **h l**. Note that help will also match partial commands if unique "
@@ -692,8 +693,8 @@ phpdbg_help_text_t phpdbg_help_text[] = {
},
{"frame",
-"The **frame** takes an optional integer argument. If omitted, then the current frame is displayed "
-"If specified then the current scope is set to the corresponding frame listed in a **back** trace. "
+"The **frame** takes an optional integer argument. If omitted, then the current frame is displayed. "
+"If specified, then the current scope is set to the corresponding frame listed in a **back** trace. "
"This can be used to allowing access to the variables in a higher stack frame than that currently being executed." CR CR
"**Examples**" CR CR
@@ -702,7 +703,25 @@ phpdbg_help_text_t phpdbg_help_text[] = {
" Go to frame 2 and print out variable **$count** in that frame" CR CR
"Note that this frame scope is discarded when execution continues, with the execution frame "
-"then reset to the lowest executiong frame."
+"then reset to the lowest executing frame."
+},
+
+{"generator",
+"The **generator** command takes an optional integer argument. If omitted, then a list of the "
+"currently active generators is displayed. If specified then the current scope is set to the frame "
+"of the generator with the corresponding object handle. This can be used to inspect any generators "
+"not in the current **back** trace." CR CR
+
+"**Examples**" CR CR
+" $P generator" CR
+" List of generators, with the #id being the object handle, e.g.:" CR
+" #3: my_generator(argument=\"value\") at test.php:5" CR
+" $P g 3" CR
+" $P ev $i" CR
+" Go to frame of generator with object handle 3 and print out variable **$i** in that frame" CR CR
+
+"Note that this frame scope is discarded when execution continues, with the execution frame "
+"then reset to the lowest executing frame."
},
{"info",
diff --git a/sapi/phpdbg/phpdbg_list.c b/sapi/phpdbg/phpdbg_list.c
index e67c5fc022..cd778562af 100644
--- a/sapi/phpdbg/phpdbg_list.c
+++ b/sapi/phpdbg/phpdbg_list.c
@@ -198,11 +198,12 @@ void phpdbg_list_function_byname(const char *str, size_t len) /* {{{ */
/* search active scope if begins with period */
if (func_name[0] == '.') {
- if (EG(scope)) {
+ zend_class_entry *scope = zend_get_executed_scope();
+ if (scope) {
func_name++;
func_name_len--;
- func_table = &EG(scope)->function_table;
+ func_table = &scope->function_table;
} else {
phpdbg_error("inactive", "type=\"noclasses\"", "No active class");
return;
@@ -376,22 +377,9 @@ zend_op_array *phpdbg_compile_string(zval *source_string, char *filename) {
return op_array;
}
-void phpdbg_free_file_source(zval *zv) {
- phpdbg_file_source *data = Z_PTR_P(zv);
-
- if (data->buf) {
- efree(data->buf);
- }
-
- destroy_op_array(&data->op_array);
-
- efree(data);
-}
-
void phpdbg_init_list(void) {
PHPDBG_G(compile_file) = zend_compile_file;
PHPDBG_G(compile_string) = zend_compile_string;
- zend_hash_init(&PHPDBG_G(file_sources), 1, NULL, (dtor_func_t) phpdbg_free_file_source, 0);
zend_compile_file = phpdbg_compile_file;
zend_compile_string = phpdbg_compile_string;
}
diff --git a/sapi/phpdbg/phpdbg_list.h b/sapi/phpdbg/phpdbg_list.h
index 01afb747b3..aaca41aeaa 100644
--- a/sapi/phpdbg/phpdbg_list.h
+++ b/sapi/phpdbg/phpdbg_list.h
@@ -48,7 +48,6 @@ typedef struct {
void *map;
#endif
zend_op_array op_array;
- zend_bool destroy_op_array;
uint lines;
uint line[1];
} phpdbg_file_source;
diff --git a/sapi/phpdbg/phpdbg_opcode.c b/sapi/phpdbg/phpdbg_opcode.c
index 54d70a7747..368784cfd4 100644
--- a/sapi/phpdbg/phpdbg_opcode.c
+++ b/sapi/phpdbg/phpdbg_opcode.c
@@ -36,7 +36,8 @@ static inline const char *phpdbg_decode_opcode(zend_uchar opcode) /* {{{ */
return "UNKNOWN";
} /* }}} */
-static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t type) /* {{{ */
+static inline char *phpdbg_decode_op(
+ zend_op_array *ops, const znode_op *op, uint32_t type) /* {{{ */
{
char *decode = NULL;
@@ -62,91 +63,57 @@ static inline char *phpdbg_decode_op(zend_op_array *ops, znode_op *op, uint32_t
return decode;
} /* }}} */
-char *phpdbg_decode_opline(zend_op_array *ops, zend_op *op) /*{{{ */
-{
- const char *opcode_name = phpdbg_decode_opcode(op->opcode);
- char *result, *decode[4] = {NULL, NULL, NULL, NULL};
-
- /* EX */
- switch (op->opcode) {
- case ZEND_FAST_CALL:
- if (op->extended_value == ZEND_FAST_CALL_FROM_FINALLY) {
- decode[0] = estrdup("FAST_CALL<FROM_FINALLY>");
+char *phpdbg_decode_input_op(
+ zend_op_array *ops, const zend_op *opline, znode_op op, zend_uchar op_type,
+ uint32_t flags) {
+ char *result = NULL;
+ if (op_type != IS_UNUSED) {
+ result = phpdbg_decode_op(ops, &op, op_type);
+ } else if (ZEND_VM_OP_JMP_ADDR == (flags & ZEND_VM_OP_MASK)) {
+ spprintf(&result, 0, "J%td", OP_JMP_ADDR(opline, op) - ops->opcodes);
+ } else if (ZEND_VM_OP_NUM == (flags & ZEND_VM_OP_MASK)) {
+ spprintf(&result, 0, "%" PRIu32, op.num);
+ } else if (ZEND_VM_OP_TRY_CATCH == (flags & ZEND_VM_OP_MASK)) {
+ if (op.num != (uint32_t)-1) {
+ spprintf(&result, 0, "try-catch(%" PRIu32 ")", op.num);
}
- break;
- case ZEND_FAST_RET:
- if (op->extended_value != 0) {
- spprintf(&decode[0], 0, "FAST_RET<%s>",
- op->extended_value == ZEND_FAST_RET_TO_CATCH ? "TO_CATCH" : "TO_FINALLY");
+ } else if (ZEND_VM_OP_LIVE_RANGE == (flags & ZEND_VM_OP_MASK)) {
+ if (opline->extended_value & ZEND_FREE_ON_RETURN) {
+ spprintf(&result, 0, "live-range(%" PRIu32 ")", op.num);
}
- break;
+ } else if (ZEND_VM_OP_THIS == (flags & ZEND_VM_OP_MASK)) {
+ result = estrdup("THIS");
+ } else if (ZEND_VM_OP_NEXT == (flags & ZEND_VM_OP_MASK)) {
+ result = estrdup("NEXT");
+ } else if (ZEND_VM_OP_CLASS_FETCH == (flags & ZEND_VM_OP_MASK)) {
+ //zend_dump_class_fetch_type(op.num);
+ } else if (ZEND_VM_OP_CONSTRUCTOR == (flags & ZEND_VM_OP_MASK)) {
+ result = estrdup("CONSTRUCTOR");
}
+ return result;
+}
- /* OP1 */
- switch (op->opcode) {
- case ZEND_JMP:
- case ZEND_FAST_CALL:
- spprintf(&decode[1], 0, "J%td", OP_JMP_ADDR(op, op->op1) - ops->opcodes);
- break;
-
- case ZEND_INIT_FCALL:
- case ZEND_RECV:
- case ZEND_RECV_INIT:
- case ZEND_RECV_VARIADIC:
- spprintf(&decode[1], 0, "%" PRIu32, op->op1.num);
- break;
+char *phpdbg_decode_opline(zend_op_array *ops, zend_op *opline) /*{{{ */
+{
+ const char *opcode_name = phpdbg_decode_opcode(opline->opcode);
+ uint32_t flags = zend_get_opcode_flags(opline->opcode);
+ char *result, *decode[4] = {NULL, NULL, NULL, NULL};
- default:
- decode[1] = phpdbg_decode_op(ops, &op->op1, op->op1_type);
- break;
- }
+ /* OP1 */
+ decode[1] = phpdbg_decode_input_op(
+ ops, opline, opline->op1, opline->op1_type, ZEND_VM_OP1_FLAGS(flags));
/* OP2 */
- switch (op->opcode) {
- case ZEND_JMPZNZ:
- spprintf(&decode[2], 0, "J%td or J%td",
- OP_JMP_ADDR(op, op->op2) - ops->opcodes,
- ZEND_OFFSET_TO_OPLINE(op, op->extended_value) - ops->opcodes);
- break;
-
- case ZEND_JMPZ:
- case ZEND_JMPNZ:
- case ZEND_JMPZ_EX:
- case ZEND_JMPNZ_EX:
- case ZEND_JMP_SET:
- case ZEND_ASSERT_CHECK:
- spprintf(&decode[2], 0, "J%td", OP_JMP_ADDR(op, op->op2) - ops->opcodes);
- break;
-
- case ZEND_FAST_CALL:
- case ZEND_FAST_RET:
- if (op->extended_value != 0) {
- spprintf(&decode[2], 0, "J%" PRIu32, op->op2.opline_num);
- }
- break;
-
- case ZEND_SEND_VAL:
- case ZEND_SEND_VAL_EX:
- case ZEND_SEND_VAR:
- case ZEND_SEND_VAR_NO_REF:
- case ZEND_SEND_REF:
- case ZEND_SEND_VAR_EX:
- case ZEND_SEND_USER:
- spprintf(&decode[2], 0, "%" PRIu32, op->op2.num);
- break;
-
- default:
- decode[2] = phpdbg_decode_op(ops, &op->op2, op->op2_type);
- break;
- }
+ decode[2] = phpdbg_decode_input_op(
+ ops, opline, opline->op2, opline->op2_type, ZEND_VM_OP2_FLAGS(flags));
/* RESULT */
- switch (op->opcode) {
+ switch (opline->opcode) {
case ZEND_CATCH:
- spprintf(&decode[3], 0, "%" PRIu32, op->result.num);
+ spprintf(&decode[3], 0, "%" PRIu32, opline->result.num);
break;
default:
- decode[3] = phpdbg_decode_op(ops, &op->result, op->result_type);
+ decode[3] = phpdbg_decode_op(ops, &opline->result, opline->result_type);
break;
}
diff --git a/sapi/phpdbg/phpdbg_print.c b/sapi/phpdbg/phpdbg_print.c
index 39c8ba29e7..829f632506 100644
--- a/sapi/phpdbg/phpdbg_print.c
+++ b/sapi/phpdbg/phpdbg_print.c
@@ -217,11 +217,13 @@ PHPDBG_PRINT(func) /* {{{ */
zend_string *lcname;
/* search active scope if begins with period */
if (func_name[0] == '.') {
- if (EG(scope)) {
+ zend_class_entry *scope = zend_get_executed_scope();
+
+ if (scope) {
func_name++;
func_name_len--;
- func_table = &EG(scope)->function_table;
+ func_table = &scope->function_table;
} else {
phpdbg_error("inactive", "type=\"noclasses\"", "No active class");
return SUCCESS;
diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c
index 9fe4fc7885..77954a96d7 100644
--- a/sapi/phpdbg/phpdbg_prompt.c
+++ b/sapi/phpdbg/phpdbg_prompt.c
@@ -69,34 +69,35 @@ extern int phpdbg_startup_run;
/* {{{ command declarations */
const phpdbg_command_t phpdbg_prompt_commands[] = {
- PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s", 0),
- PHPDBG_COMMAND_D(stdin, "read script from stdin", 0 , NULL, "s", 0),
- PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0, PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(continue,"continue execution", 'c', NULL, 0, PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s", 0),
- PHPDBG_COMMAND_D(ev, "evaluate some code", 0 , NULL, "i", PHPDBG_ASYNC_SAFE), /* restricted ASYNC_SAFE */
- PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0, 0),
- PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0, 0),
- PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0, 0),
- PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, "|*c", 0),
- PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c", 0),
- PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n", PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n", PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*", PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "|s", PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0, 0),
- PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0, 0),
- PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s", PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s", PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(register,"register a function", 'R', NULL, "s", 0),
- PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s", 0),
- PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s", PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(sh, "shell a command", 0 , NULL, "i", 0),
- PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0, PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(wait, "wait for other process", 'W', NULL, 0, 0),
- PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss", 0),
- PHPDBG_COMMAND_D(next, "step over next line", 'n', NULL, 0, PHPDBG_ASYNC_SAFE),
- PHPDBG_COMMAND_D(eol, "set EOL", 'E', NULL, "|s", 0),
+ PHPDBG_COMMAND_D(exec, "set execution context", 'e', NULL, "s", 0),
+ PHPDBG_COMMAND_D(stdin, "read script from stdin", 0 , NULL, "s", 0),
+ PHPDBG_COMMAND_D(step, "step through execution", 's', NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(continue, "continue execution", 'c', NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(run, "attempt execution", 'r', NULL, "|s", 0),
+ PHPDBG_COMMAND_D(ev, "evaluate some code", 0 , NULL, "i", PHPDBG_ASYNC_SAFE), /* restricted ASYNC_SAFE */
+ PHPDBG_COMMAND_D(until, "continue past the current line", 'u', NULL, 0, 0),
+ PHPDBG_COMMAND_D(finish, "continue past the end of the stack", 'F', NULL, 0, 0),
+ PHPDBG_COMMAND_D(leave, "continue until the end of the stack", 'L', NULL, 0, 0),
+ PHPDBG_COMMAND_D(generator, "inspect or switch to a generator", 'g', NULL, "|n", 0),
+ PHPDBG_COMMAND_D(print, "print something", 'p', phpdbg_print_commands, "|*c", 0),
+ PHPDBG_COMMAND_D(break, "set breakpoint", 'b', phpdbg_break_commands, "|*c", 0),
+ PHPDBG_COMMAND_D(back, "show trace", 't', NULL, "|n", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(frame, "switch to a frame", 'f', NULL, "|n", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(list, "lists some code", 'l', phpdbg_list_commands, "*", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(info, "displays some informations", 'i', phpdbg_info_commands, "|s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(clean, "clean the execution environment", 'X', NULL, 0, 0),
+ PHPDBG_COMMAND_D(clear, "clear breakpoints", 'C', NULL, 0, 0),
+ PHPDBG_COMMAND_D(help, "show help menu", 'h', phpdbg_help_commands, "|s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(set, "set phpdbg configuration", 'S', phpdbg_set_commands, "s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(register, "register a function", 'R', NULL, "s", 0),
+ PHPDBG_COMMAND_D(source, "execute a phpdbginit", '<', NULL, "s", 0),
+ PHPDBG_COMMAND_D(export, "export breaks to a .phpdbginit script", '>', NULL, "s", PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(sh, "shell a command", 0 , NULL, "i", 0),
+ PHPDBG_COMMAND_D(quit, "exit phpdbg", 'q', NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(wait, "wait for other process", 'W', NULL, 0, 0),
+ PHPDBG_COMMAND_D(watch, "set watchpoint", 'w', phpdbg_watch_commands, "|ss", 0),
+ PHPDBG_COMMAND_D(next, "step over next line", 'n', NULL, 0, PHPDBG_ASYNC_SAFE),
+ PHPDBG_COMMAND_D(eol, "set EOL", 'E', NULL, "|s", 0),
PHPDBG_END_COMMAND
}; /* }}} */
@@ -123,9 +124,7 @@ static inline int phpdbg_call_register(phpdbg_param_t *stack) /* {{{ */
ZVAL_STRINGL(&fci.function_name, lc_name, name->len);
fci.size = sizeof(zend_fcall_info);
- fci.function_table = &PHPDBG_G(registered);
//???fci.symbol_table = zend_rebuild_symbol_table();
- fci.symbol_table = NULL;
fci.object = NULL;
fci.retval = &fretval;
fci.no_separation = 1;
@@ -794,7 +793,7 @@ static inline void phpdbg_handle_exception(void) /* {{{ */
phpdbg_error("exception", "name=\"%s\" file=\"%s\" line=\"" ZEND_LONG_FMT "\"", "Uncaught %s in %s on line " ZEND_LONG_FMT, ZSTR_VAL(ex->ce->name), ZSTR_VAL(file), line);
zend_string_release(file);
- phpdbg_writeln("exceptionmsg", "msg=\"%s\"", ZSTR_VAL(msg));
+ phpdbg_writeln("exceptionmsg", "msg=\"%s\"", "%s", ZSTR_VAL(msg));
zend_string_release(msg);
if (EG(prev_exception)) {
@@ -831,10 +830,13 @@ PHPDBG_COMMAND(run) /* {{{ */
if (param && param->type != EMPTY_PARAM && param->len != 0) {
char **argv = emalloc(5 * sizeof(char *));
char *end = param->str + param->len, *p = param->str;
+ char last_byte;
int argc = 0;
int i;
while (*end == '\r' || *end == '\n') *(end--) = 0;
+ last_byte = end[1];
+ end[1] = 0;
while (*p == ' ') p++;
while (*p) {
@@ -898,6 +900,7 @@ free_cmd:
efree(argv[i]);
}
efree(argv);
+ end[1] = last_byte;
return SUCCESS;
}
@@ -908,6 +911,8 @@ free_cmd:
do p++; while (*p == ' ');
}
}
+ end[1] = last_byte;
+
argv[0] = SG(request_info).argv[0];
for (i = SG(request_info).argc; --i;) {
efree(SG(request_info).argv[i]);
@@ -920,7 +925,7 @@ free_cmd:
}
/* clean up from last execution */
- if (ex && ex->symbol_table) {
+ if (ex && (ZEND_CALL_INFO(ex) & ZEND_CALL_HAS_SYMBOL_TABLE)) {
zend_hash_clean(ex->symbol_table);
} else {
zend_rebuild_symbol_table();
@@ -1004,7 +1009,6 @@ PHPDBG_COMMAND(ev) /* {{{ */
zval retval;
zend_execute_data *original_execute_data = EG(current_execute_data);
- zend_class_entry *original_scope = EG(scope);
zend_vm_stack original_stack = EG(vm_stack);
zend_object *ex = NULL;
@@ -1052,7 +1056,6 @@ PHPDBG_COMMAND(ev) /* {{{ */
OBJ_RELEASE(ex);
}
EG(current_execute_data) = original_execute_data;
- EG(scope) = original_scope;
EG(vm_stack_top) = original_stack->top;
EG(vm_stack_end) = original_stack->end;
EG(vm_stack) = original_stack;
@@ -1089,6 +1092,56 @@ PHPDBG_COMMAND(back) /* {{{ */
return SUCCESS;
} /* }}} */
+PHPDBG_COMMAND(generator) /* {{{ */
+{
+ int i;
+
+ if (!PHPDBG_G(in_execution)) {
+ phpdbg_error("inactive", "type=\"noexec\"", "Not executing!");
+ return SUCCESS;
+ }
+
+ if (param) {
+ i = param->num;
+ zend_object **obj = EG(objects_store).object_buckets + i;
+ if (i < EG(objects_store).top && *obj && IS_OBJ_VALID(*obj) && (*obj)->ce == zend_ce_generator) {
+ zend_generator *gen = (zend_generator *) *obj;
+ if (gen->execute_data) {
+ if (zend_generator_get_current(gen)->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
+ phpdbg_error("generator", "type=\"running\"", "Generator currently running");
+ } else {
+ phpdbg_open_generator_frame(gen);
+ }
+ } else {
+ phpdbg_error("generator", "type=\"closed\"", "Generator already closed");
+ }
+ } else {
+ phpdbg_error("invalidarg", "", "Invalid object handle");
+ }
+ } else {
+ for (i = 0; i < EG(objects_store).top; i++) {
+ zend_object *obj = EG(objects_store).object_buckets[i];
+ if (obj && IS_OBJ_VALID(obj) && obj->ce == zend_ce_generator) {
+ zend_generator *gen = (zend_generator *) obj, *current = zend_generator_get_current(gen);
+ if (gen->execute_data) {
+ zend_string *s = phpdbg_compile_stackframe(gen->execute_data);
+ phpdbg_out("#%d: %.*s", i, (int) ZSTR_LEN(s), ZSTR_VAL(s));
+ zend_string_release(s);
+ if (gen != current) {
+ if (gen->node.parent != current) {
+ phpdbg_out(" with direct parent #%d and", gen->node.parent->std.handle);
+ }
+ phpdbg_out(" executing #%d currently", current->std.handle);
+ }
+ phpdbg_out("\n");
+ }
+ }
+ }
+ }
+
+ return SUCCESS;
+} /* }}} */
+
PHPDBG_COMMAND(print) /* {{{ */
{
if (!param || param->type == EMPTY_PARAM) {
@@ -1541,9 +1594,7 @@ PHPDBG_COMMAND(watch) /* {{{ */
phpdbg_list_watchpoints();
} else switch (param->type) {
case STR_PARAM:
- if (phpdbg_create_var_watchpoint(param->str, param->len) != FAILURE) {
- phpdbg_notice("watch", "variable=\"%.*s\"", "Set watchpoint on %.*s", (int) param->len, param->str);
- }
+ phpdbg_create_var_watchpoint(param->str, param->len);
break;
phpdbg_default_switch_case();
@@ -1849,7 +1900,7 @@ next:
execute_data->call->func->type == ZEND_USER_FUNCTION) {
zend_execute_ex = execute_ex;
}
- PHPDBG_G(vmret) = zend_vm_call_opcode_handler(execute_data);
+ PHPDBG_G(vmret) = zend_vm_call_opcode_handler(execute_data);
zend_execute_ex = phpdbg_execute_ex;
if (PHPDBG_G(vmret) != 0) {
diff --git a/sapi/phpdbg/phpdbg_prompt.h b/sapi/phpdbg/phpdbg_prompt.h
index df26f83e2d..3d6fd491c2 100644
--- a/sapi/phpdbg/phpdbg_prompt.h
+++ b/sapi/phpdbg/phpdbg_prompt.h
@@ -52,6 +52,7 @@ PHPDBG_COMMAND(clear);
PHPDBG_COMMAND(help);
PHPDBG_COMMAND(sh);
PHPDBG_COMMAND(dl);
+PHPDBG_COMMAND(generator);
PHPDBG_COMMAND(set);
PHPDBG_COMMAND(source);
PHPDBG_COMMAND(export);
diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c
index d953543aee..1fd059d685 100644
--- a/sapi/phpdbg/phpdbg_utils.c
+++ b/sapi/phpdbg/phpdbg_utils.c
@@ -532,7 +532,7 @@ PHPDBG_API int phpdbg_parse_variable_with_arg(char *input, size_t len, HashTable
last_index[index_len] = 0;
if (!(zv = zend_symtable_str_find(parent, last_index, index_len))) {
if (!silent) {
- phpdbg_error("variable", "type=\"undefined\" variable=\"%.*s\"", "%.*s is undefined", (int) i, input);
+ phpdbg_error("variable", "type=\"undefined\" variable=\"%.*s\"", "%.*s is undefined", (int) input[i] == ']' ? i + 1 : i, input);
}
return FAILURE;
}
@@ -772,7 +772,7 @@ PHPDBG_API zend_bool phpdbg_check_caught_ex(zend_execute_data *execute_data, zen
return 1;
}
- catch = cur->extended_value;
+ catch += cur->extended_value / sizeof(zend_op);
} while (!cur->result.num);
return 0;
@@ -804,6 +804,18 @@ char *phpdbg_short_zval_print(zval *zv, int maxlen) /* {{{ */
break;
case IS_DOUBLE:
spprintf(&decode, 0, "%.*G", 14, Z_DVAL_P(zv));
+
+ /* Make sure it looks like a float */
+ if (zend_finite(Z_DVAL_P(zv)) && !strchr(decode, '.')) {
+ size_t len = strlen(decode);
+ char *decode2 = emalloc(len + strlen(".0") + 1);
+ memcpy(decode2, decode, len);
+ decode2[len] = '.';
+ decode2[len+1] = '0';
+ decode2[len+2] = '\0';
+ efree(decode);
+ decode = decode2;
+ }
break;
case IS_STRING: {
int i;
diff --git a/sapi/phpdbg/phpdbg_watch.c b/sapi/phpdbg/phpdbg_watch.c
index a2d13a6883..6267f4fadf 100644
--- a/sapi/phpdbg/phpdbg_watch.c
+++ b/sapi/phpdbg/phpdbg_watch.c
@@ -20,53 +20,86 @@
/* Some information for the reader...
*
- * Watchpoints are either simple, recursive or implicit (PHPDBG_WATCH_* flags)
+ * The main structure managing the direct observations is the watchpoint (phpdbg_watchpoint_t). There are several types of watchpoints currently:
+ * WATCH_ON_BUCKET: a watchpoint on a Bucket element, used to monitor values inside HashTables (largely handled equivalently to WATCH_ON_ZVAL, it just monitors also for IS_UNDEF and key changes)
+ * WATCH_ON_ZVAL: a watchpoint on a bare zval (&zend_reference.val, zval.value.indirect)
+ * WATCH_ON_STR: a watchpoint on a zend_string (put on &ZSTR_LEN() in order to not watch refcount/hash)
+ * WATCH_ON_HASHTABLE: a watchpoint on a HashTable (currently only used to observe size changes, put after flags in order to not watch refcount)
+ * WATCH_ON_REFCOUNTED: a watchpoint on a zend_refcounted, observes the refcount and serves as reference pointer in the custom efree handler
+ * WATCH_ON_HASHDATA: special watchpoint to watch for HT_GET_DATA_ADDR(ht) being efree()'d to be able to properly relocate Bucket watches
+ *
+ * Watch elements are either simple, recursive or implicit (PHPDBG_WATCH_* flags)
* Simple means that a particular watchpoint was explicitely defined
- * Recursive watchpoints are created recursively and substitute simple watchpoints
- * Implicit watchpoints are implicitely created on all ancestors of simple or recursive watchpoints
- * Recursive and (simple or implicit) watchpoints are mutually exclusive
+ * Recursive watch elements are created recursively (recursive root flag is to distinguish the root element easily from its children recursive elements)
+ * Implicit watch elements are implicitely created on all ancestors of simple or recursive watch elements
+ * Recursive and (simple or implicit) watch elements are mutually exclusive
+ * Array/Object to distinguish watch elements on arrays
+ *
+ * Watch elements all contain a reference to a watchpoint (except if scheduled for recreation); a "watch" is a watch element created by the user with a specific id
+ * Each watch has its independent structure of watch elements, watchpoints are responsible for managing collisions and preventing pointers being watched multiple times
*
* PHPDBG_G(watchpoint_tree) contains all watchpoints identified by the watch target address
- * PHPDBG_G(watch_HashTables) contains the dtors of the HashTables to call in our custom dtor (we substitute the dtor of HashTables containing watched zvals by our own dtor)
- * PHPDBG_G(watchpoints) contains all watchpoints (except the ones managed by watch collision)
- * PHPDBG_G(watch_collisions) is indexed by a zend_reference * pointer. It stores information about collisions (everything which contains a zend_reference * may be referenced by multiple watches)
+ * PHPDBG_G(watch_HashTables) contains the addresses of parent_containers of watch elements
+ * PHPDBG_G(watch_elements) contains all directly defined watch elements (i.e. those which have an individual id)
+ * PHPDBG_G(watch_collisions) is indexed by a zend_refcounted * pointer (phpdbg_watchpoint_t.ref). It stores information about collisions (everything which contains a zend_refcounted * may be referenced by multiple watches)
+ * PHPDBG_G(watch_free) is a set of pointers to watch for being freed (like HashTables referenced by phpdbg_watch_element.parent_container)
+ * PHPDBG_G(watch_recreation) is the list of watch elements whose watchpoint has been removed (via efree() for example) and needs to be recreated
+ * PHPDBG_G(watchlist_mem) is the list of unprotected memory pages; used to watch which pages need their PROT_WRITE attribute removed after checking
*
- * Creating a watchpoint:
- * * Create watchpoints with PHPDBG_WATCH_IMPLICIT set on each zval and HashTable in hierarchy except the last zval or HashTable fetch. (if already existing PHPDBG_WATCH_IMPLICIT flag is added)
- * * Create a PHPDBG_WATCH_SIMPLE watch for simple watches or a PHPDBG_WATCH_RECURSIVE watch for recursive watches
- * * When the target zval is an IS_REFERENCE, create a watchpoint on it too
- * * Each time a watchpoints parent is a zval and it is Z_REFCOUNTED(), put a watchpoint (WATCH_ON_REFCOUNTED) on it and add a watchpoint collision
- * * When in recursive mode and encountering a not-refcounted PHPDBG_WATCH_SIMPLE, remove it and recreate it with a PHPDBG_WATCH_RECURSIVE (handled via watch collision)
- * * Make attention to not add something twice or iterate over it twice
+ * Watching on addresses:
+ * * Address and size are transformed into memory page aligned address and size
+ * * mprotect() enables or disables them (depending on flags) - Windows has a transparent compatibility layer in phpdbg_win.c
+ * * segfault handler stores the address of the page and marks it again as writable
+ * * later watchpoints pointing inside these pages are compared against their current value and eventually reactivated (or deleted)
*
- * Deleting a watchpoint:
- * * Only allow deletion of recursive watches at their root and simple watches
- * * Go to referenced variable. And remove watch collision on *parent* (if there is a parent)
- * * If it is Z_REFCOUNTED(), remove that watch collision
+ * Creating a watch:
+ * * Implicit watch elements for each element in the hierarchy (starting from base, which typically is current symbol table) except the last one
+ * * Create a watch element with either simple flag or recursive [+ root] flags
+ * * If the element has recursive flag, create elements recursively for every referenced HashTable and zval
+ *
+ * Creating a watch element:
+ * * For each watch element a related watchpoint is created, if there's none yet; add itself then into the list of parents of that watchpoint
+ * * If the watch has a parent_container, add itself also into a phpdbg_watch_ht_info (inside PHPDBG_G(watch_HashTables)) [and creates it if not yet existing]
+ *
+ * Creation of watchpoints:
+ * * Watchpoints create a watch collision for each refcounted or indirect on the zval (if type is WATCH_ON_BUCKET or WATCH_ON_ZVAL)
+ * * Backs the current value of the watched pointer up
+ * * Installs the watchpoint in PHPDBG_G(watchpoint_tree) and activates it (activation of a watchpoint = remove PROT_WRITE from the pages the watched pointer resides on)
*
* Watch collisions:
- * * hold a counter for recursive, if it is incremented from 0 to 1, create recursive watchpoint
- * * holds a HashTable for normal (not implicit) watchpoints ... it is used to get the fetch type of the HashTable (depending on whether it is empty or not)
- * * holds a HashTable for implicit watchpoints ... (some sort of a refcounter, but ensure that there are no duplicates)
- * * if normal and implicit watchpoints are empty, drop that watch collision and remove WATCH_ON_REFCOUNT alongside with watchpoint on an eventual reference
+ * * Manages a watchpoint on the refcount (WATCH_ON_REFCOUNTED) or indirect zval (WATCH_ON_ZVAL)
+ * * Guarantees that every pointer is watched at most once (by having a pointer to collision mapping in PHPDBG_G(watch_collisions), which have the unique watchpoints for the respective collision)
+ * * Contains a list of parents, i.e. which watchpoints reference it (via watch->ref)
+ * * If no watchpoint is referencing it anymore, the watch collision and its associated watchpoints (phpdbg_watch_collision.ref/reference) are removed
*
- * Watching on addresses:
- * * Address and size are transformed into memory page aligned address and size
- * * mprotect() enables or disables them (depending on flags) - Windows has a transparent compatibility layer in phpdbg_win.c
- * * segfault handler dumps watched memory segment and deactivates watchpoint
- * * later watches inside these memory segments are compared against their current value and eventually reactivated (or deleted)
+ * Deleting a watch:
+ * * Watches are stored by an id in PHPDBG_G(watch_elements); the associated watch element is then deleted
+ * * Deletes all parent and children implicit watch elements
+ *
+ * Deleting a watch element:
+ * * Removes itself from the parent list of the associated watchpoints; if that parent list is empty, also delete the watchpoint
+ * * Removes itself from the related phpdbg_watch_ht_info if it has a parent_container
+ *
+ * Deleting a watchpoint:
+ * * Remove itself from watch collisions this watchpoint participates in
+ * * Removes the watchpoint from PHPDBG_G(watchpoint_tree) and deactivates it (deactivation of a watchpoint = add PROT_WRITE to the pages the watched pointer resides on)
+ *
+ * A watched pointer is efree()'d:
+ * * Needs immediate action as we else may run into dereferencing a pointer into freed memory
+ * * Deletes the associated watchpoint, and for each watch element, if recursive, all its children elements
+ * * If the its watch elements are implicit, recursive roots or simple, they and all their children are dissociated from their watchpoints (i.e. removed from the watchpoint, if no other element is referencing it, it is deleted); adds these elements to PHPDBG_G(watch_recreation)
*
- * A watched zval was removed:
- * * trigger a memory copy (in segv handler) and an automatic deactivation
- * * change a type flag _zval_struct.u1.v.type_flags (add PHPDBG_DESTRUCTED_ZVAL flag) in memory dump
+ * Recreating watchpoints:
+ * * Upon each opcode, PHPDBG_G(watch_recreation) is checked and all its elements are searched for whether the watch is still reachable via the tree given by its implicits
+ * * In case they are not reachable, the watch is deleted (and thus all the related watch elements), else a new watchpoint is created for all the watch elements
+ * * The old and new values of the watches are compared and shown if changed
*
- * A watched zval was changed:
- * * check if parent container has a different reference for referenced zval - recursively update all watches and drop them if necessary
- * * if _zval_struct.u1.v.type_flags & PHPDBG_DESTRUCTED_ZVAL, add it to a list of zvals to be handled at the end (if location was not changed, remove it finally)
- * * display changes if watch->flags & PHPDBG_WATCH_NORMAL (means: not implicit)
- * * handle case where Z_RECOUNTED() or Z_PTR() changed (remove/add collison(s))
- * * if necessary ... on zvals: handle references, if recursive also objects and arrays ... on arrays: if recursive, add new elements
- * * drop destructed zval watchpoints which were not updated
+ * Comparing watchpoints:
+ * * The old and new values of the watches are compared and shown if changed
+ * * If changed, it is checked whether the refcounted/indirect changed and watch collisions removed or created accordingly
+ * * If a zval/bucket watchpoint is recursive, watch elements are added or removed accordingly
+ * * If an array watchpoint is recursive, new array watchpoints are added if there are new ones in the array
+ * * If the watch (element with an id) is not reachable anymore due to changes in implicits, the watch is removed
*/
#include "zend.h"
@@ -84,32 +117,111 @@ ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
const phpdbg_command_t phpdbg_watch_commands[] = {
PHPDBG_COMMAND_D_EX(array, "create watchpoint on an array", 'a', watch_array, &phpdbg_prompt_commands[24], "s", 0),
- PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, &phpdbg_prompt_commands[24], "s", 0),
+ PHPDBG_COMMAND_D_EX(delete, "delete watchpoint", 'd', watch_delete, &phpdbg_prompt_commands[24], "n", 0),
PHPDBG_COMMAND_D_EX(recursive, "create recursive watchpoints", 'r', watch_recursive, &phpdbg_prompt_commands[24], "s", 0),
PHPDBG_END_COMMAND
};
-//#define HT_FROM_WATCH(watch) (watch->type == WATCH_ON_OBJECT ? watch->addr.obj->handlers->get_properties(watch->parent_container.zv) : watch->type == WATCH_ON_ARRAY ? &watch->addr.arr->ht : NULL)
#define HT_FROM_ZVP(zvp) (Z_TYPE_P(zvp) == IS_OBJECT ? Z_OBJPROP_P(zvp) : Z_TYPE_P(zvp) == IS_ARRAY ? Z_ARRVAL_P(zvp) : NULL)
#define HT_WATCH_OFFSET (sizeof(zend_refcounted *) + sizeof(uint32_t)) /* we are not interested in gc and flags */
#define HT_PTR_HT(ptr) ((HashTable *) (((char *) (ptr)) - HT_WATCH_OFFSET))
#define HT_WATCH_HT(watch) HT_PTR_HT((watch)->addr.ptr)
-typedef struct {
- void *page;
- size_t size;
- char reenable_writing;
- /* data must be last element */
- void *data;
-} phpdbg_watch_memdump;
+/* ### PRINTING POINTER DIFFERENCES ### */
+zend_bool phpdbg_check_watch_diff(phpdbg_watchtype type, void *oldPtr, void *newPtr) {
+ switch (type) {
+ case WATCH_ON_BUCKET:
+ if (memcmp(&((Bucket *) oldPtr)->h, &((Bucket *) newPtr)->h, sizeof(Bucket) - sizeof(zval) /* key/val comparison */) != 0) {
+ return 2;
+ }
+ case WATCH_ON_ZVAL:
+ return memcmp(oldPtr, newPtr, sizeof(zend_value) + sizeof(uint32_t) /* value + typeinfo */) != 0;
+ case WATCH_ON_HASHTABLE:
+ return zend_hash_num_elements(HT_PTR_HT(oldPtr)) != zend_hash_num_elements(HT_PTR_HT(newPtr));
+ case WATCH_ON_REFCOUNTED:
+ return memcmp(oldPtr, newPtr, sizeof(uint32_t) /* no zend_refcounted metadata info */) != 0;
+ case WATCH_ON_STR:
+ return memcmp(oldPtr, newPtr, *(size_t *) oldPtr + XtOffsetOf(zend_string, val) - XtOffsetOf(zend_string, len)) != 0;
+ case WATCH_ON_HASHDATA:
+ ZEND_ASSERT(0);
+ }
+ return 0;
+}
+
+void phpdbg_print_watch_diff(phpdbg_watchtype type, zend_string *name, void *oldPtr, void *newPtr) {
+ int32_t elementDiff;
+
+ PHPDBG_G(watchpoint_hit) = 1;
-#define MEMDUMP_SIZE(size) (sizeof(phpdbg_watch_memdump) - sizeof(void *) + (size))
+ phpdbg_notice("watchhit", "variable=\"%s\"", "Breaking on watchpoint %.*s", (int) ZSTR_LEN(name), ZSTR_VAL(name));
+ phpdbg_xml("<watchdata %r>");
+ switch (type) {
+ case WATCH_ON_BUCKET:
+ case WATCH_ON_ZVAL:
+ if (Z_REFCOUNTED_P((zval *) oldPtr)) {
+ phpdbg_writeln("watchvalue", "type=\"old\" inaccessible=\"inaccessible\"", "Old value inaccessible or destroyed");
+ } else if (Z_TYPE_P((zval *) oldPtr) == IS_INDIRECT) {
+ phpdbg_writeln("watchvalue", "type=\"old\" inaccessible=\"inaccessible\"", "Old value inaccessible or destroyed (was indirect)");
+ } else {
+ phpdbg_out("Old value: ");
+ phpdbg_xml("<watchvalue %r type=\"old\">");
+ zend_print_flat_zval_r((zval *) oldPtr);
+ phpdbg_xml("</watchvalue>");
+ phpdbg_out("\n");
+ }
+
+ while (Z_TYPE_P((zval *) newPtr) == IS_INDIRECT) {
+ newPtr = Z_INDIRECT_P((zval *) newPtr);
+ }
+
+ phpdbg_out("New value%s: ", Z_ISREF_P((zval *) newPtr) ? " (reference)" : "");
+ phpdbg_xml("<watchvalue %r%s type=\"new\">", Z_ISREF_P((zval *) newPtr) ? " reference=\"reference\"" : "");
+ zend_print_flat_zval_r((zval *) newPtr);
+ phpdbg_xml("</watchvalue>");
+ phpdbg_out("\n");
+ break;
+ case WATCH_ON_HASHTABLE:
+ elementDiff = zend_hash_num_elements(HT_PTR_HT(oldPtr)) - zend_hash_num_elements(HT_PTR_HT(newPtr));
+ if (elementDiff > 0) {
+ phpdbg_writeln("watchsize", "removed=\"%d\"", "%d elements were removed from the array", (int) elementDiff);
+ } else if (elementDiff < 0) {
+ phpdbg_writeln("watchsize", "added=\"%d\"", "%d elements were added to the array", (int) -elementDiff);
+ }
+ break;
+
+ case WATCH_ON_REFCOUNTED:
+ phpdbg_writeln("watchrefcount", "type=\"old\" refcount=\"%d\"", "Old refcount: %d", GC_REFCOUNT((zend_refcounted *) oldPtr));
+ phpdbg_writeln("watchrefcount", "type=\"new\" refcount=\"%d\"", "New refcount: %d", GC_REFCOUNT((zend_refcounted *) newPtr));
+ break;
+
+ case WATCH_ON_STR:
+ phpdbg_out("Old value: ");
+ phpdbg_xml("<watchvalue %r type=\"old\">");
+ zend_write((char *) oldPtr + XtOffsetOf(zend_string, val) - XtOffsetOf(zend_string, len), *(size_t *) oldPtr);
+ phpdbg_xml("</watchvalue>");
+ phpdbg_out("\n");
+
+ phpdbg_out("New value: ");
+ phpdbg_xml("<watchvalue %r type=\"new\">");
+ zend_write((char *) newPtr + XtOffsetOf(zend_string, val) - XtOffsetOf(zend_string, len), *(size_t *) newPtr);
+ phpdbg_xml("</watchvalue>");
+ phpdbg_out("\n");
+ break;
+
+ case WATCH_ON_HASHDATA:
+ ZEND_ASSERT(0);
+ }
+
+ phpdbg_xml("</watchdata>");
+}
+
+/* ### LOW LEVEL WATCHPOINT HANDLING ### */
static phpdbg_watchpoint_t *phpdbg_check_for_watchpoint(void *addr) {
phpdbg_watchpoint_t *watch;
- phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong)phpdbg_get_page_boundary(addr) + phpdbg_pagesize - 1);
+ phpdbg_btree_result *result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong) phpdbg_get_page_boundary(addr) + phpdbg_pagesize - 1);
if (result == NULL) {
return NULL;
@@ -139,509 +251,1007 @@ static inline void phpdbg_deactivate_watchpoint(phpdbg_watchpoint_t *watch) {
phpdbg_change_watchpoint_access(watch, PROT_READ | PROT_WRITE);
}
-static inline void phpdbg_store_watchpoint(phpdbg_watchpoint_t *watch) {
+/* Note that consecutive pages need to be merged in order to avoid watchpoints spanning page boundaries to have part of their data in the one page, part in the other page */
+#ifdef _WIN32
+int phpdbg_watchpoint_segfault_handler(void *addr) {
+#else
+int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context) {
+#endif
+
+ void *page = phpdbg_get_page_boundary(
+#ifdef _WIN32
+ addr
+#else
+ info->si_addr
+#endif
+ );
+
+ /* perhaps unnecessary, but check to be sure to not conflict with other segfault handlers */
+ if (phpdbg_check_for_watchpoint(page) == NULL) {
+ return FAILURE;
+ }
+
+ /* re-enable writing */
+ mprotect(page, phpdbg_pagesize, PROT_READ | PROT_WRITE);
+
+ zend_hash_index_add_empty_element(PHPDBG_G(watchlist_mem), (zend_ulong) page);
+
+ return SUCCESS;
+}
+
+/* ### REGISTER WATCHPOINT ### To be used only by watch element and collision managers ### */
+static inline void phpdbg_store_watchpoint_btree(phpdbg_watchpoint_t *watch) {
+ phpdbg_btree_result *res;
+ ZEND_ASSERT((res = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) watch->addr.ptr)) == NULL || res->ptr == watch);
phpdbg_btree_insert(&PHPDBG_G(watchpoint_tree), (zend_ulong) watch->addr.ptr, watch);
}
-static inline void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch) {
+static inline void phpdbg_remove_watchpoint_btree(phpdbg_watchpoint_t *watch) {
phpdbg_btree_delete(&PHPDBG_G(watchpoint_tree), (zend_ulong) watch->addr.ptr);
}
-void phpdbg_create_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) {
+/* ### SET WATCHPOINT ADDR ### To be used only by watch element and collision managers ### */
+void phpdbg_set_addr_watchpoint(void *addr, size_t size, phpdbg_watchpoint_t *watch) {
watch->addr.ptr = addr;
watch->size = size;
+ watch->ref = NULL;
+ watch->coll = NULL;
+ zend_hash_init(&watch->elements, 8, brml, NULL, 0);
}
-void phpdbg_create_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch) {
- phpdbg_create_addr_watchpoint(zv, sizeof(zval), watch);
+void phpdbg_set_zval_watchpoint(zval *zv, phpdbg_watchpoint_t *watch) {
+ phpdbg_set_addr_watchpoint(zv, sizeof(zval) - sizeof(uint32_t), watch);
watch->type = WATCH_ON_ZVAL;
}
-void phpdbg_create_ht_watchpoint(HashTable *ht, phpdbg_watchpoint_t *watch) {
- phpdbg_create_addr_watchpoint(((char *) ht) + HT_WATCH_OFFSET, sizeof(HashTable) - HT_WATCH_OFFSET, watch);
- watch->type = WATCH_ON_HASHTABLE;
- watch->implicit_ht_count = 0;
+void phpdbg_set_bucket_watchpoint(Bucket *bucket, phpdbg_watchpoint_t *watch) {
+ phpdbg_set_addr_watchpoint(bucket, sizeof(Bucket), watch);
+ watch->type = WATCH_ON_BUCKET;
}
-static int phpdbg_create_recursive_ht_watch(phpdbg_watchpoint_t *watch);
-static int phpdbg_create_recursive_zval_watch(phpdbg_watchpoint_t *watch);
-
-void phpdbg_watch_HashTable_dtor(zval *ptr);
+void phpdbg_set_ht_watchpoint(HashTable *ht, phpdbg_watchpoint_t *watch) {
+ phpdbg_set_addr_watchpoint(((char *) ht) + HT_WATCH_OFFSET, sizeof(HashTable) - HT_WATCH_OFFSET, watch);
+ watch->type = WATCH_ON_HASHTABLE;
+}
-static void phpdbg_free_watch(phpdbg_watchpoint_t *watch) {
- zend_string_release(watch->str);
- zend_string_release(watch->name_in_parent);
+void phpdbg_watch_backup_data(phpdbg_watchpoint_t *watch) {
+ switch (watch->type) {
+ case WATCH_ON_BUCKET:
+ case WATCH_ON_ZVAL:
+ case WATCH_ON_REFCOUNTED:
+ memcpy(&watch->backup, watch->addr.ptr, watch->size);
+ break;
+ case WATCH_ON_STR:
+ if (watch->backup.str) {
+ zend_string_release(watch->backup.str);
+ }
+ watch->backup.str = zend_string_init((char *) watch->addr.ptr + XtOffsetOf(zend_string, val) - XtOffsetOf(zend_string, len), *(size_t *) watch->addr.ptr, 1);
+ break;
+ case WATCH_ON_HASHTABLE:
+ memcpy((char *) &watch->backup + HT_WATCH_OFFSET, watch->addr.ptr, watch->size);
+ case WATCH_ON_HASHDATA:
+ break;
+ }
}
-static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch);
-static void phpdbg_delete_ht_watchpoints_recursive(phpdbg_watchpoint_t *watch);
-static void phpdbg_delete_zval_watchpoints_recursive(phpdbg_watchpoint_t *watch);
-static void phpdbg_delete_watchpoints_recursive(phpdbg_watchpoint_t *watch);
-
-/* Store all the possible watches the refcounted may refer to (for displaying & deleting by identifier) [collision] */
-static phpdbg_watchpoint_t *phpdbg_create_refcounted_watchpoint(phpdbg_watchpoint_t *parent, zend_refcounted *ref) {
- phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
- watch->flags = parent->flags;
- watch->parent = parent;
- watch->str = parent->str;
- ++GC_REFCOUNT(parent->str);
- phpdbg_create_addr_watchpoint(&GC_REFCOUNT(ref), sizeof(uint32_t), watch);
- watch->type = WATCH_ON_REFCOUNTED;
+/* ### MANAGE WATCH COLLISIONS ### To be used only by watch element manager and memory differ ### */
+/* watch collisions are responsible for having only one watcher on a given refcounted/refval and having a mapping back to the parent zvals */
+void phpdbg_delete_watch_collision(phpdbg_watchpoint_t *watch) {
+ phpdbg_watch_collision *coll;
+ if ((coll = zend_hash_index_find_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) watch->ref))) {
+ zend_hash_index_del(&coll->parents, (zend_ulong) watch);
+ if (zend_hash_num_elements(&coll->parents) == 0) {
+ phpdbg_deactivate_watchpoint(&coll->ref);
+ phpdbg_remove_watchpoint_btree(&coll->ref);
+
+ if (coll->ref.type == WATCH_ON_ZVAL) {
+ phpdbg_delete_watch_collision(&coll->ref);
+ } else if (coll->reference.addr.ptr) {
+ phpdbg_deactivate_watchpoint(&coll->reference);
+ phpdbg_remove_watchpoint_btree(&coll->reference);
+ phpdbg_delete_watch_collision(&coll->reference);
+ if (coll->reference.type == WATCH_ON_STR) {
+ zend_string_release(coll->reference.backup.str);
+ }
+ }
- return watch;
+ zend_hash_index_del(&PHPDBG_G(watch_collisions), (zend_ulong) watch->ref);
+ zend_hash_destroy(&coll->parents);
+ efree(coll);
+ }
+ }
}
-/* Must prevent duplicates ... if there are duplicates, replace new by old! */
-static void phpdbg_add_watch_collision(phpdbg_watchpoint_t *watch) {
- phpdbg_watch_collision *cur;
+void phpdbg_update_watch_ref(phpdbg_watchpoint_t *watch) {
+ phpdbg_watch_collision *coll;
- /* Check for either recursive or (simple and/or implicit) */
- ZEND_ASSERT(((watch->flags & PHPDBG_WATCH_RECURSIVE) == 0) ^ ((watch->flags & (PHPDBG_WATCH_IMPLICIT | PHPDBG_WATCH_SIMPLE)) == 0));
+ ZEND_ASSERT(watch->type == WATCH_ON_ZVAL || watch->type == WATCH_ON_BUCKET);
+ if (Z_REFCOUNTED_P(watch->addr.zv)) {
+ if (Z_COUNTED_P(watch->addr.zv) == watch->ref) {
+ return;
+ }
- if ((cur = zend_hash_index_find_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) watch->addr.ref))) {
- phpdbg_watchpoint_t *old;
- int flags = 0;
- if ((old = zend_hash_find_ptr(&cur->watches, watch->str)) || (old = zend_hash_find_ptr(&cur->implicit_watches, watch->str))) {
- if (((old->flags ^ watch->flags) & (PHPDBG_WATCH_NORMAL|PHPDBG_WATCH_IMPLICIT)) == 0) {
- return; /* there was no change ... */
+ if (watch->ref != NULL) {
+ phpdbg_delete_watch_collision(watch);
+ }
+
+ watch->ref = Z_COUNTED_P(watch->addr.zv);
+
+ if (!(coll = zend_hash_index_find_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) watch->ref))) {
+ coll = emalloc(sizeof(*coll));
+ coll->ref.type = WATCH_ON_REFCOUNTED;
+ phpdbg_set_addr_watchpoint(Z_COUNTED_P(watch->addr.zv), sizeof(uint32_t), &coll->ref);
+ coll->ref.coll = coll;
+ phpdbg_store_watchpoint_btree(&coll->ref);
+ phpdbg_activate_watchpoint(&coll->ref);
+ phpdbg_watch_backup_data(&coll->ref);
+
+ if (Z_ISREF_P(watch->addr.zv)) {
+ phpdbg_set_zval_watchpoint(Z_REFVAL_P(watch->addr.zv), &coll->reference);
+ coll->reference.coll = coll;
+ phpdbg_update_watch_ref(&coll->reference);
+ phpdbg_store_watchpoint_btree(&coll->reference);
+ phpdbg_activate_watchpoint(&coll->reference);
+ phpdbg_watch_backup_data(&coll->reference);
+ } else if (Z_TYPE_P(watch->addr.zv) == IS_STRING) {
+ coll->reference.type = WATCH_ON_STR;
+ phpdbg_set_addr_watchpoint(&Z_STRLEN_P(watch->addr.zv), XtOffsetOf(zend_string, val) - XtOffsetOf(zend_string, len) + Z_STRLEN_P(watch->addr.zv) + 1, &coll->reference);
+ coll->reference.coll = coll;
+ phpdbg_store_watchpoint_btree(&coll->reference);
+ phpdbg_activate_watchpoint(&coll->reference);
+ coll->reference.backup.str = NULL;
+ phpdbg_watch_backup_data(&coll->reference);
+ } else {
+ coll->reference.addr.ptr = NULL;
}
- flags = old->flags;
+ zend_hash_init(&coll->parents, 8, shitty stupid parameter, NULL, 0);
+ zend_hash_index_add_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) watch->ref, coll);
+ }
+ zend_hash_index_add_ptr(&coll->parents, (zend_long) watch, watch);
+ } else if (Z_TYPE_P(watch->addr.zv) == IS_INDIRECT) {
+ if ((zend_refcounted *) Z_INDIRECT_P(watch->addr.zv) == watch->ref) {
+ return;
+ }
- if (flags & PHPDBG_WATCH_RECURSIVE) {
- if (!(watch->flags & PHPDBG_WATCH_RECURSIVE) && !--cur->refs) {
- phpdbg_delete_watchpoints_recursive(watch);
- }
- }
- if (flags & PHPDBG_WATCH_NORMAL) {
- zend_hash_del(&cur->watches, watch->str);
- if (zend_hash_num_elements(&cur->watches) > 0) {
- cur->watch = Z_PTR_P(zend_hash_get_current_data_ex(&cur->watches, NULL));
- } else {
- cur->watch = Z_PTR_P(zend_hash_get_current_data_ex(&cur->implicit_watches, NULL));
- }
- }
- if (flags & PHPDBG_WATCH_IMPLICIT) {
- zend_hash_del(&cur->implicit_watches, watch->str);
- }
+ if (watch->ref != NULL) {
+ phpdbg_delete_watch_collision(watch);
+ }
- old->flags = watch->flags;
- phpdbg_free_watch(watch);
- efree(watch);
- watch = old;
+ watch->ref = (zend_refcounted *) Z_INDIRECT_P(watch->addr.zv);
+
+ if (!(coll = zend_hash_index_find_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) watch->ref))) {
+ coll = emalloc(sizeof(*coll));
+ phpdbg_set_zval_watchpoint(Z_INDIRECT_P(watch->addr.zv), &coll->ref);
+ coll->ref.coll = coll;
+ phpdbg_update_watch_ref(&coll->ref);
+ phpdbg_store_watchpoint_btree(&coll->ref);
+ phpdbg_activate_watchpoint(&coll->ref);
+ phpdbg_watch_backup_data(&coll->ref);
+
+ zend_hash_init(&coll->parents, 8, shitty stupid parameter, NULL, 0);
+ zend_hash_index_add_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) watch->ref, coll);
}
- if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
- if (!(flags & PHPDBG_WATCH_RECURSIVE) && !cur->refs++) {
- phpdbg_create_recursive_zval_watch(watch->parent);
- }
+ zend_hash_index_add_ptr(&coll->parents, (zend_long) watch, watch);
+ } else if (watch->ref) {
+ phpdbg_delete_watch_collision(watch);
+ watch->ref = NULL;
+ }
+}
+
+/* ### MANAGE WATCH ELEMENTS ### */
+/* watchpoints must be unique per element. Only one watchpoint may point to one element. But many elements may point to one watchpoint. */
+void phpdbg_recurse_watch_element(phpdbg_watch_element *element);
+void phpdbg_remove_watch_element_recursively(phpdbg_watch_element *element);
+void phpdbg_free_watch_element(phpdbg_watch_element *element);
+void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch);
+void phpdbg_watch_parent_ht(phpdbg_watch_element *element);
+
+phpdbg_watch_element *phpdbg_add_watch_element(phpdbg_watchpoint_t *watch, phpdbg_watch_element *element) {
+ phpdbg_btree_result *res;
+ if ((res = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) watch->addr.ptr)) == NULL) {
+ phpdbg_watchpoint_t *mem = emalloc(sizeof(*mem));
+ *mem = *watch;
+ watch = mem;
+ phpdbg_store_watchpoint_btree(watch);
+ if (watch->type == WATCH_ON_ZVAL || watch->type == WATCH_ON_BUCKET) {
+ phpdbg_update_watch_ref(watch);
}
+ phpdbg_activate_watchpoint(watch);
+ phpdbg_watch_backup_data(watch);
} else {
- phpdbg_watch_collision coll;
- coll.refs = (watch->flags & PHPDBG_WATCH_RECURSIVE) != 0;
- coll.watch = watch;
- zend_hash_init(&coll.watches, 8, arghs, NULL, 0);
- zend_hash_init(&coll.implicit_watches, 8, ..., NULL, 0);
- cur = zend_hash_index_add_mem(&PHPDBG_G(watch_collisions), (zend_ulong) watch->addr.ref, &coll, sizeof(phpdbg_watch_collision));
- phpdbg_store_watchpoint(cur->watch);
- phpdbg_activate_watchpoint(cur->watch);
- if (coll.refs) {
- phpdbg_create_recursive_zval_watch(watch->parent);
+ phpdbg_watch_element *old_element;
+ watch = res->ptr;
+ if ((old_element = zend_hash_find_ptr(&watch->elements, element->str))) {
+ phpdbg_free_watch_element(element);
+ return old_element;
}
}
- if (watch->flags & PHPDBG_WATCH_NORMAL) {
- cur->watch = watch;
- zend_hash_add_ptr(&cur->watches, watch->str, watch->parent);
+ element->watch = watch;
+ zend_hash_add_ptr(&watch->elements, element->str, element);
+
+ if (element->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_recurse_watch_element(element);
}
- if (watch->flags & PHPDBG_WATCH_IMPLICIT) {
- zend_hash_add_ptr(&cur->implicit_watches, watch->str, watch->parent);
+
+ return element;
+}
+
+phpdbg_watch_element *phpdbg_add_bucket_watch_element(Bucket *bucket, phpdbg_watch_element *element) {
+ phpdbg_watchpoint_t watch;
+ phpdbg_set_bucket_watchpoint(bucket, &watch);
+ element = phpdbg_add_watch_element(&watch, element);
+ phpdbg_watch_parent_ht(element);
+ return element;
+}
+
+phpdbg_watch_element *phpdbg_add_ht_watch_element(zval *zv, phpdbg_watch_element *element) {
+ phpdbg_watchpoint_t watch;
+ HashTable *ht = HT_FROM_ZVP(zv);
+
+ if (!ht) {
+ return NULL;
}
+
+ element->flags |= Z_TYPE_P(zv) == IS_ARRAY ? PHPDBG_WATCH_ARRAY : PHPDBG_WATCH_OBJECT;
+ phpdbg_set_ht_watchpoint(ht, &watch);
+ return phpdbg_add_watch_element(&watch, element);
}
-static void phpdbg_remove_watch_collision(phpdbg_watchpoint_t *watch) {
- phpdbg_watch_collision *cur;
- if ((cur = zend_hash_index_find_ptr(&PHPDBG_G(watch_collisions), (zend_ulong) Z_COUNTED_P(watch->addr.zv)))) {
- if (cur->refs && !--cur->refs) {
- phpdbg_delete_watchpoints_recursive(watch);
+zend_bool phpdbg_is_recursively_watched(void *ptr, phpdbg_watch_element *element) {
+ phpdbg_watch_element *next = element;
+ do {
+ element = next;
+ if (element->watch->addr.ptr == ptr) {
+ return 1;
}
+ next = element->parent;
+ } while (!(element->flags & PHPDBG_WATCH_RECURSIVE_ROOT));
- zend_hash_del(&cur->watches, watch->str);
- zend_hash_del(&cur->implicit_watches, watch->str);
+ return 0;
+}
- if (zend_hash_num_elements(&cur->watches) > 0) {
- cur->watch = Z_PTR_P(zend_hash_get_current_data_ex(&cur->watches, NULL));
- } else if (zend_hash_num_elements(&cur->implicit_watches) > 0) {
- cur->watch = Z_PTR_P(zend_hash_get_current_data_ex(&cur->implicit_watches, NULL));
- } else {
- phpdbg_deactivate_watchpoint(cur->watch);
- phpdbg_remove_watchpoint(cur->watch);
+void phpdbg_add_recursive_watch_from_ht(phpdbg_watch_element *element, zend_long idx, zend_string *str, zval *zv) {
+ phpdbg_watch_element *child;
+ if (phpdbg_is_recursively_watched(zv, element)) {
+ return;
+ }
- zend_hash_index_del(&PHPDBG_G(watch_collisions), (zend_ulong) Z_COUNTED_P(watch->addr.zv));
- }
+ child = emalloc(sizeof(*child));
+ child->flags = PHPDBG_WATCH_RECURSIVE;
+ if (str) {
+ child->str = strpprintf(0, (element->flags & PHPDBG_WATCH_ARRAY) ? "%.*s[%s]" : "%.*s->%s", (int) ZSTR_LEN(element->str) - 2, ZSTR_VAL(element->str), phpdbg_get_property_key(ZSTR_VAL(str)));
+ } else {
+ child->str = strpprintf(0, (element->flags & PHPDBG_WATCH_ARRAY) ? "%.*s[" ZEND_LONG_FMT "]" : "%.*s->" ZEND_LONG_FMT, (int) ZSTR_LEN(element->str) - 2, ZSTR_VAL(element->str), idx);
}
+ if (!str) {
+ str = zend_long_to_str(idx); // TODO: hack, use proper int handling for name in parent
+ } else { str = zend_string_copy(str); }
+ child->name_in_parent = str;
+ child->parent = element;
+ child->child = NULL;
+ child->parent_container = HT_WATCH_HT(element->watch);
+ zend_hash_add_ptr(&element->child_container, child->str, child);
+ phpdbg_add_bucket_watch_element((Bucket *) zv, child);
}
-static phpdbg_watchpoint_t *phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch);
-
-static phpdbg_watchpoint_t *phpdbg_create_reference_watch(phpdbg_watchpoint_t *watch) {
- phpdbg_watchpoint_t *ref = emalloc(sizeof(phpdbg_watchpoint_t));
- watch->reference = ref;
- ref->flags = watch->flags;
- ref->str = watch->str;
- ++GC_REFCOUNT(ref->str);
- ref->parent = watch;
- ref->parent_container = NULL;
- phpdbg_create_zval_watchpoint(Z_REFVAL_P(watch->addr.zv), ref);
+void phpdbg_recurse_watch_element(phpdbg_watch_element *element) {
+ phpdbg_watch_element *child;
+ zval *zv;
- phpdbg_create_watchpoint(ref);
+ if (element->watch->type == WATCH_ON_ZVAL || element->watch->type == WATCH_ON_BUCKET) {
+ zv = element->watch->addr.zv;
+ while (Z_TYPE_P(zv) == IS_INDIRECT) {
+ zv = Z_INDIRECT_P(zv);
+ }
+ ZVAL_DEREF(zv);
- return ref;
-}
+ if (element->child) {
+ phpdbg_remove_watch_element_recursively(element->child);
+ }
-static phpdbg_watchpoint_t *phpdbg_get_refcount_watch(phpdbg_watchpoint_t *parent) {
- zend_refcounted *ref;
- phpdbg_btree_result *res;
+ if ((Z_TYPE_P(zv) != IS_ARRAY && Z_TYPE_P(zv) != IS_OBJECT)
+ || phpdbg_is_recursively_watched(HT_WATCH_OFFSET + (char *) HT_FROM_ZVP(zv), element)) {
+ if (element->child) {
+ phpdbg_free_watch_element(element->child);
+ element->child = NULL;
+ }
+ return;
+ }
- if (parent->type == WATCH_ON_HASHTABLE) {
- parent = parent->parent;
- if (!parent) {
- return NULL;
+ if (element->child) {
+ child = element->child;
+ } else {
+ child = emalloc(sizeof(*child));
+ child->flags = PHPDBG_WATCH_RECURSIVE;
+ child->str = strpprintf(0, "%.*s[]", (int) ZSTR_LEN(element->str), ZSTR_VAL(element->str));
+ child->name_in_parent = NULL;
+ child->parent = element;
+ child->child = NULL;
+ element->child = child;
}
+ zend_hash_init(&child->child_container, 8, NULL, NULL, 0);
+ phpdbg_add_ht_watch_element(zv, child);
+ } else if (zend_hash_num_elements(&element->child_container) == 0) {
+ zend_string *str;
+ zend_long idx;
+
+ ZEND_ASSERT(element->watch->type == WATCH_ON_HASHTABLE);
+ ZEND_HASH_FOREACH_KEY_VAL(HT_WATCH_HT(element->watch), idx, str, zv) {
+ phpdbg_add_recursive_watch_from_ht(element, idx, str, zv);
+ } ZEND_HASH_FOREACH_END();
}
+}
+
+void phpdbg_watch_parent_ht(phpdbg_watch_element *element) {
+ if (element->watch->type == WATCH_ON_BUCKET) {
+ phpdbg_btree_result *res;
+ HashPosition pos;
+ phpdbg_watch_ht_info *hti;
+ ZEND_ASSERT(element->parent_container);
+ if (!(res = phpdbg_btree_find(&PHPDBG_G(watch_HashTables), (zend_ulong) element->parent_container))) {
+ hti = emalloc(sizeof(*hti));
+ hti->ht = element->parent_container;
+
+ zend_hash_init(&hti->watches, 0, grrrrr, ZVAL_PTR_DTOR, 0);
+ phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong) hti->ht, hti);
+
+ phpdbg_set_addr_watchpoint(HT_GET_DATA_ADDR(hti->ht), HT_HASH_SIZE(hti->ht->nTableMask), &hti->hash_watch);
+ hti->hash_watch.type = WATCH_ON_HASHDATA;
+ phpdbg_store_watchpoint_btree(&hti->hash_watch);
+ phpdbg_activate_watchpoint(&hti->hash_watch);
+ } else {
+ hti = (phpdbg_watch_ht_info *) res->ptr;
+ }
- ZEND_ASSERT(parent->type == WATCH_ON_ZVAL);
- ref = Z_COUNTED_P(parent->addr.zv);
+ zend_hash_internal_pointer_end_ex(hti->ht, &pos);
+ hti->last = hti->ht->arData + pos;
+ hti->last_str = hti->last->key;
+ hti->last_idx = hti->last->h;
- res = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) ref);
- if (res) {
- return res->ptr;
+ zend_hash_add_ptr(&hti->watches, element->name_in_parent, element);
}
- return NULL;
}
-static phpdbg_watchpoint_t *phpdbg_create_watchpoint(phpdbg_watchpoint_t *watch) {
- phpdbg_watchpoint_t *ret = watch;
+void phpdbg_unwatch_parent_ht(phpdbg_watch_element *element) {
+ if (element->watch->type == WATCH_ON_BUCKET) {
+ phpdbg_btree_result *res = phpdbg_btree_find(&PHPDBG_G(watch_HashTables), (zend_ulong) element->parent_container);
+ ZEND_ASSERT(element->parent_container);
+ if (res) {
+ phpdbg_watch_ht_info *hti = res->ptr;
- if (watch->type == WATCH_ON_ZVAL) {
- switch (Z_TYPE_P(watch->addr.zv)) {
- case IS_NULL:
- case IS_UNDEF:
- case IS_TRUE:
- case IS_FALSE:
- memset(watch->addr.zv, 0, sizeof(zend_value));
+ if (zend_hash_num_elements(&hti->watches) == 1) {
+ zend_hash_destroy(&hti->watches);
+ phpdbg_btree_delete(&PHPDBG_G(watch_HashTables), (zend_ulong) hti->ht);
+ phpdbg_deactivate_watchpoint(&hti->hash_watch);
+ phpdbg_remove_watchpoint_btree(&hti->hash_watch);
+ efree(hti);
+ } else {
+ zend_hash_del(&hti->watches, element->name_in_parent);
+ }
}
}
+}
+
+/* ### DE/QUEUE WATCH ELEMENTS ### to be used by watch element manager only */
+/* implicit watchpoints may change (especially because of separation); elements updated by remove & re-add etc.; thus we need to wait a little bit (until next opcode) and then compare whether the watchpoint still exists and if not, remove it */
+
+void phpdbg_dissociate_watch_element(phpdbg_watch_element *element, phpdbg_watch_element *until);
+void phpdbg_free_watch_element_tree(phpdbg_watch_element *element);
- /* exclude references & refcounted */
- if (!watch->parent || watch->parent->type != WATCH_ON_ZVAL || watch->type == WATCH_ON_HASHTABLE) {
- phpdbg_watchpoint_t *old_watch = zend_hash_find_ptr(&PHPDBG_G(watchpoints), watch->str);
-
- if (old_watch) {
-#define return_and_free_watch(x) { \
- phpdbg_watchpoint_t *ref = phpdbg_get_refcount_watch(old_watch); \
- if (ref) { \
- phpdbg_add_watch_collision(ref); \
- } \
- if (watch != old_watch) { \
- phpdbg_free_watch(watch); \
- efree(watch); \
- } \
- return (x); \
+void phpdbg_queue_element_for_recreation(phpdbg_watch_element *element) {
+ /* store lowermost element */
+ phpdbg_watch_element *prev;
+
+ if ((prev = zend_hash_find_ptr(&PHPDBG_G(watch_recreation), element->str))) {
+ phpdbg_watch_element *child = prev;
+ do {
+ if (child == element) {
+ return;
+ }
+ child = child->child;
+ } while (child);
+ }
+ zend_hash_update_ptr(&PHPDBG_G(watch_recreation), element->str, element);
+
+ /* dissociate from watchpoint to avoid dangling memory watches */
+ phpdbg_dissociate_watch_element(element, prev);
+
+ if (!element->parent) {
+ /* HERE BE DRAGONS; i.e. we assume HashTable is directly allocated via emalloc() ... (which *should be* the case for every user-accessible array and symbol tables) */
+ zend_hash_index_add_empty_element(&PHPDBG_G(watch_free), (zend_ulong) element->parent_container);
+ }
}
- if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
- if (old_watch->flags & PHPDBG_WATCH_RECURSIVE) {
- return_and_free_watch(NULL);
- } else {
- old_watch->flags &= ~(PHPDBG_WATCH_SIMPLE | PHPDBG_WATCH_IMPLICIT);
- old_watch->flags |= PHPDBG_WATCH_RECURSIVE;
- return_and_free_watch(old_watch);
- }
- } else {
- if (!(old_watch->flags & PHPDBG_WATCH_RECURSIVE)) {
- old_watch->flags |= watch->flags & (PHPDBG_WATCH_IMPLICIT | PHPDBG_WATCH_SIMPLE);
- }
- return_and_free_watch(NULL);
+
+zend_bool phpdbg_try_readding_watch_element(zval *parent, phpdbg_watch_element *element) {
+ zval *zv;
+ HashTable *ht = HT_FROM_ZVP(parent);
+
+ if (!ht) {
+ return 0;
+ } else if (element->flags & (PHPDBG_WATCH_ARRAY | PHPDBG_WATCH_OBJECT)) {
+ char *htPtr = ((char *) ht) + HT_WATCH_OFFSET;
+ char *oldPtr = ((char *) &element->backup.ht) + HT_WATCH_OFFSET;
+ if (phpdbg_check_watch_diff(WATCH_ON_HASHTABLE, oldPtr, htPtr)) {
+ phpdbg_print_watch_diff(WATCH_ON_HASHTABLE, element->str, oldPtr, htPtr);
+ }
+
+ phpdbg_add_ht_watch_element(parent, element);
+ } else if ((zv = zend_symtable_find(ht, element->name_in_parent))) {
+ if (element->flags & PHPDBG_WATCH_IMPLICIT) {
+ zval *next = zv;
+
+ while (Z_TYPE_P(next) == IS_INDIRECT) {
+ next = Z_INDIRECT_P(next);
}
- } else {
- if (watch->parent && watch->parent->type == WATCH_ON_HASHTABLE) {
- watch->parent->implicit_ht_count++;
+ if (Z_ISREF_P(next)) {
+ next = Z_REFVAL_P(next);
}
- zend_hash_add_ptr(&PHPDBG_G(watchpoints), watch->str, watch);
+
+ if (!phpdbg_try_readding_watch_element(next, element->child)) {
+ return 0;
+ }
+ } else if (phpdbg_check_watch_diff(WATCH_ON_ZVAL, &element->backup.zv, zv)) {
+ phpdbg_print_watch_diff(WATCH_ON_ZVAL, element->str, &element->backup.zv, zv);
}
+
+ element->parent_container = ht;
+ phpdbg_add_bucket_watch_element((Bucket *) zv, element);
+ phpdbg_watch_parent_ht(element);
+ } else {
+ return 0;
}
- phpdbg_store_watchpoint(watch);
+ return 1;
+}
- if (watch->parent && watch->parent->type == WATCH_ON_ZVAL && Z_REFCOUNTED_P(watch->parent->addr.zv)) {
- phpdbg_add_watch_collision(phpdbg_create_refcounted_watchpoint(watch, Z_COUNTED_P(watch->parent->addr.zv)));
+void phpdbg_automatic_dequeue_free(phpdbg_watch_element *element) {
+ phpdbg_watch_element *child = element;
+ while (child->child && !(child->flags & PHPDBG_WATCH_RECURSIVE_ROOT)) {
+ child = child->child;
}
+ PHPDBG_G(watchpoint_hit) = 1;
+ phpdbg_notice("watchdelete", "variable=\"%.*s\" recursive=\"%s\"", "%.*s has been removed, removing watchpoint%s", (int) ZSTR_LEN(child->str), ZSTR_VAL(child->str), (child->flags & PHPDBG_WATCH_RECURSIVE_ROOT) ? " recursively" : "");
+ zend_hash_index_del(&PHPDBG_G(watch_elements), child->id);
+ phpdbg_free_watch_element_tree(element);
+}
- if (watch->type == WATCH_ON_ZVAL) {
- if (watch->parent_container) {
- HashTable *ht_watches;
- phpdbg_btree_result *find;
- if (!(find = phpdbg_btree_find(&PHPDBG_G(watch_HashTables), (zend_ulong) watch->parent_container))) {
- phpdbg_watch_ht_info *hti = emalloc(sizeof(*hti));
- hti->dtor = watch->parent_container->pDestructor;
- ht_watches = &hti->watches;
- zend_hash_init(ht_watches, 0, grrrrr, ZVAL_PTR_DTOR, 0);
- phpdbg_btree_insert(&PHPDBG_G(watch_HashTables), (zend_ulong) watch->parent_container, hti);
- watch->parent_container->pDestructor = (dtor_func_t) phpdbg_watch_HashTable_dtor;
+void phpdbg_dequeue_elements_for_recreation() {
+ phpdbg_watch_element *element;
+
+ ZEND_HASH_FOREACH_PTR(&PHPDBG_G(watch_recreation), element) {
+ ZEND_ASSERT(element->flags & (PHPDBG_WATCH_IMPLICIT | PHPDBG_WATCH_RECURSIVE_ROOT | PHPDBG_WATCH_SIMPLE));
+ if (element->parent || zend_hash_index_find(&PHPDBG_G(watch_free), (zend_ulong) element->parent_container)) {
+ zval _zv, *zv = &_zv;
+ if (element->parent) {
+ ZEND_ASSERT(element->parent->watch->type == WATCH_ON_ZVAL || element->parent->watch->type == WATCH_ON_BUCKET);
+ zv = element->parent->watch->addr.zv;
+ while (Z_TYPE_P(zv) == IS_INDIRECT) {
+ zv = Z_INDIRECT_P(zv);
+ }
+ ZVAL_DEREF(zv);
} else {
- ht_watches = &((phpdbg_watch_ht_info *) find->ptr)->watches;
+ ZVAL_ARR(zv, element->parent_container);
+ }
+ if (!phpdbg_try_readding_watch_element(zv, element)) {
+ phpdbg_automatic_dequeue_free(element);
}
- zend_hash_add_ptr(ht_watches, watch->name_in_parent, watch);
+ } else {
+ phpdbg_automatic_dequeue_free(element);
}
+ } ZEND_HASH_FOREACH_END();
- if (Z_ISREF_P(watch->addr.zv)) {
- ret = phpdbg_create_reference_watch(watch);
- }
- }
+ zend_hash_clean(&PHPDBG_G(watch_recreation));
+ zend_hash_clean(&PHPDBG_G(watch_free));
+}
- phpdbg_activate_watchpoint(watch);
+/* ### WATCH ELEMENT DELETION ### only use phpdbg_remove_watch_element from the exterior */
+void phpdbg_clean_watch_element(phpdbg_watch_element *element);
- return ret;
+void phpdbg_free_watch_element(phpdbg_watch_element *element) {
+ zend_string_release(element->str);
+ if (element->name_in_parent) {
+ zend_string_release(element->name_in_parent);
+ }
+ efree(element);
}
-static int phpdbg_create_simple_watchpoint(phpdbg_watchpoint_t *watch) {
- watch->flags |= PHPDBG_WATCH_SIMPLE;
+/* note: does *not* free the passed element, only clean */
+void phpdbg_remove_watch_element_recursively(phpdbg_watch_element *element) {
+ if (element->child) {
+ phpdbg_remove_watch_element_recursively(element->child);
+ phpdbg_free_watch_element(element->child);
+ element->child = NULL;
+ } else if (element->flags & (PHPDBG_WATCH_ARRAY | PHPDBG_WATCH_OBJECT)) {
+ phpdbg_watch_element *child;
+ ZEND_HASH_FOREACH_PTR(&element->child_container, child) {
+ phpdbg_remove_watch_element_recursively(child);
+ phpdbg_free_watch_element(child);
+ } ZEND_HASH_FOREACH_END();
+ zend_hash_destroy(&element->child_container);
+ }
- phpdbg_create_watchpoint(watch);
+ phpdbg_clean_watch_element(element);
+}
- return SUCCESS;
+/* remove single watch (i.e. manual unset) or implicit removed */
+void phpdbg_remove_watch_element(phpdbg_watch_element *element) {
+ phpdbg_watch_element *parent = element->parent, *child = element->child;
+ while (parent) {
+ phpdbg_watch_element *cur = parent;
+ parent = parent->parent;
+ phpdbg_clean_watch_element(cur);
+ phpdbg_free_watch_element(cur);
+ }
+ while (child) {
+ phpdbg_watch_element *cur = child;
+ child = child->child;
+ if (cur->flags & PHPDBG_WATCH_RECURSIVE_ROOT) {
+ phpdbg_remove_watch_element_recursively(cur);
+ child = NULL;
+ } else {
+ phpdbg_clean_watch_element(cur);
+ }
+ phpdbg_free_watch_element(cur);
+ }
+ if (element->flags & PHPDBG_WATCH_RECURSIVE_ROOT) {
+ phpdbg_remove_watch_element_recursively(element);
+ } else {
+ phpdbg_clean_watch_element(element);
+ }
+ zend_hash_index_del(&PHPDBG_G(watch_elements), element->id);
+ phpdbg_free_watch_element(element);
}
-static int phpdbg_create_array_watchpoint(phpdbg_watchpoint_t *zv_watch) {
- zval *zv = zv_watch->addr.zv;
- phpdbg_watchpoint_t *watch = emalloc(sizeof(phpdbg_watchpoint_t));
- HashTable *ht = HT_FROM_ZVP(zv);
+void phpdbg_backup_watch_element(phpdbg_watch_element *element) {
+ memcpy(&element->backup, &element->watch->backup, /* element->watch->size */ sizeof(element->backup));
+}
- watch->parent = zv_watch;
+/* until argument to prevent double remove of children elements */
+void phpdbg_dissociate_watch_element(phpdbg_watch_element *element, phpdbg_watch_element *until) {
+ phpdbg_watch_element *child = element;
+ ZEND_ASSERT((element->flags & (PHPDBG_WATCH_RECURSIVE_ROOT | PHPDBG_WATCH_RECURSIVE)) != PHPDBG_WATCH_RECURSIVE);
- if (!ht) {
- return FAILURE;
+ if (element->flags & PHPDBG_WATCH_RECURSIVE_ROOT) {
+ phpdbg_backup_watch_element(element);
+ phpdbg_remove_watch_element_recursively(element);
+ return;
}
- phpdbg_create_ht_watchpoint(ht, watch);
-
- if (phpdbg_create_watchpoint(watch) == NULL) {
- return SUCCESS;
+ while (child->child != until) {
+ child = child->child;
+ if (child->flags & PHPDBG_WATCH_RECURSIVE_ROOT) {
+ phpdbg_backup_watch_element(child);
+ phpdbg_remove_watch_element_recursively(child);
+ child->child = NULL;
+ break;
+ }
+ if (child->child == NULL || (child->flags & PHPDBG_WATCH_RECURSIVE_ROOT)) {
+ phpdbg_backup_watch_element(child);
+ }
+ phpdbg_clean_watch_element(child);
}
-
- if (Z_TYPE_P(zv) == IS_ARRAY) {
- watch->flags |= PHPDBG_WATCH_ARRAY;
- } else {
- watch->flags |= PHPDBG_WATCH_OBJECT;
+ /* element needs to be removed last! */
+ if (element->child == NULL) {
+ phpdbg_backup_watch_element(element);
}
+ phpdbg_clean_watch_element(element);
+}
- phpdbg_add_watch_collision(phpdbg_create_refcounted_watchpoint(watch, Z_COUNTED_P(zv)));
-
- return SUCCESS;
+/* unlike phpdbg_remove_watch_element this *only* frees and does not clean up element + children! Only use after previous cleanup (e.g. phpdbg_dissociate_watch_element) */
+void phpdbg_free_watch_element_tree(phpdbg_watch_element *element) {
+ phpdbg_watch_element *parent = element->parent, *child = element->child;
+ while (parent) {
+ phpdbg_watch_element *cur = parent;
+ parent = parent->parent;
+ phpdbg_clean_watch_element(cur);
+ phpdbg_free_watch_element(cur);
+ }
+ while (child) {
+ phpdbg_watch_element *cur = child;
+ child = child->child;
+ phpdbg_free_watch_element(cur);
+ }
+ phpdbg_free_watch_element(element);
}
-static int phpdbg_create_recursive_watchpoint(phpdbg_watchpoint_t *watch) {
- if (watch->type != WATCH_ON_ZVAL) {
- return FAILURE;
+void phpdbg_update_watch_element_watch(phpdbg_watch_element *element) {
+ if (element->flags & PHPDBG_WATCH_IMPLICIT) {
+ phpdbg_watch_element *child = element->child;
+ while (child->flags & PHPDBG_WATCH_IMPLICIT) {
+ child = child->child;
+ }
+
+ ZEND_ASSERT(element->watch->type == WATCH_ON_ZVAL || element->watch->type == WATCH_ON_BUCKET);
+ phpdbg_queue_element_for_recreation(element);
+ } else if (element->flags & (PHPDBG_WATCH_RECURSIVE_ROOT | PHPDBG_WATCH_SIMPLE)) {
+ phpdbg_queue_element_for_recreation(element);
+ } else if (element->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_remove_watch_element_recursively(element);
+ if (element->parent->flags & (PHPDBG_WATCH_OBJECT | PHPDBG_WATCH_ARRAY)) {
+ zend_hash_del(&element->parent->child_container, element->str);
+ } else {
+ element->parent->child = NULL;
+ }
+ phpdbg_free_watch_element(element);
}
+}
- watch->flags |= PHPDBG_WATCH_RECURSIVE;
- watch = phpdbg_create_watchpoint(watch);
+void phpdbg_update_watch_collision_elements(phpdbg_watchpoint_t *watch) {
+ phpdbg_watchpoint_t *parent;
+ phpdbg_watch_element *element;
- return SUCCESS;
+ ZEND_HASH_FOREACH_PTR(&watch->coll->parents, parent) {
+ if (parent->coll) {
+ phpdbg_update_watch_collision_elements(parent);
+ } else {
+ ZEND_HASH_FOREACH_PTR(&parent->elements, element) {
+ phpdbg_update_watch_element_watch(element);
+ } ZEND_HASH_FOREACH_END();
+ }
+ } ZEND_HASH_FOREACH_END();
}
-static int phpdbg_create_recursive_ht_watch(phpdbg_watchpoint_t *watch) {
- zval *zv;
- zend_string *key;
- zend_long h;
+void phpdbg_remove_watchpoint(phpdbg_watchpoint_t *watch) {
+ phpdbg_watch_element *element;
- ZEND_ASSERT(watch->type == WATCH_ON_HASHTABLE);
+ phpdbg_deactivate_watchpoint(watch);
+ phpdbg_remove_watchpoint_btree(watch);
+ phpdbg_delete_watch_collision(watch);
- ZEND_HASH_FOREACH_KEY_VAL(HT_WATCH_HT(watch), h, key, zv) {
- phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t));
+ if (watch->coll) {
+ phpdbg_update_watch_collision_elements(watch);
+ return;
+ }
- new_watch->flags = PHPDBG_WATCH_RECURSIVE;
- new_watch->parent = watch;
- new_watch->parent_container = HT_WATCH_HT(watch);
+ watch->elements.nNumOfElements++; /* dirty hack to avoid double free */
+ ZEND_HASH_FOREACH_PTR(&watch->elements, element) {
+ phpdbg_update_watch_element_watch(element);
+ } ZEND_HASH_FOREACH_END();
+ zend_hash_destroy(&watch->elements);
- if (key) {
- new_watch->name_in_parent = key;
- ++GC_REFCOUNT(key);
- } else {
- new_watch->name_in_parent = strpprintf(0, ZEND_LONG_FMT, h);
- }
+ efree(watch);
+}
- new_watch->str = strpprintf(0, "%.*s%s%s%s", (int) ZSTR_LEN(watch->str) - 2, ZSTR_VAL(watch->str), (watch->flags & PHPDBG_WATCH_ARRAY) ? "[" : "->", phpdbg_get_property_key(ZSTR_VAL(new_watch->name_in_parent)), (watch->flags & PHPDBG_WATCH_ARRAY) ? "]" : "");
+void phpdbg_clean_watch_element(phpdbg_watch_element *element) {
+ HashTable *elements = &element->watch->elements;
+ phpdbg_unwatch_parent_ht(element);
+ zend_hash_del(elements, element->str);
+ if (zend_hash_num_elements(elements) == 0) {
+ phpdbg_remove_watchpoint(element->watch);
+ }
+}
- while (Z_TYPE_P(zv) == IS_INDIRECT) {
- zv = Z_INDIRECT_P(zv);
+/* TODO: compile a name of all hit watchpoints (ids ??) */
+zend_string *phpdbg_watchpoint_change_collision_name(phpdbg_watchpoint_t *watch) {
+ phpdbg_watchpoint_t *parent;
+ phpdbg_watch_element *element;
+ zend_string *name = NULL;
+ if (watch->coll) {
+ ZEND_HASH_FOREACH_PTR(&watch->coll->parents, parent) {
+ if (name) {
+ zend_string_release(name);
+ }
+ name = phpdbg_watchpoint_change_collision_name(parent);
+ } ZEND_HASH_FOREACH_END();
+ return name;
+ }
+ ZEND_HASH_FOREACH_PTR(&watch->elements, element) {
+ if (element->flags & PHPDBG_WATCH_IMPLICIT) {
+ if ((watch->type == WATCH_ON_ZVAL || watch->type == WATCH_ON_BUCKET) && Z_TYPE(watch->backup.zv) > IS_STRING) {
+ phpdbg_update_watch_element_watch(element->child);
+ }
+ continue;
}
-
- phpdbg_create_zval_watchpoint(zv, new_watch);
- new_watch = phpdbg_create_watchpoint(new_watch);
- phpdbg_create_recursive_zval_watch(new_watch);
+ name = element->str;
} ZEND_HASH_FOREACH_END();
- return SUCCESS;
+ return name ? zend_string_copy(name) : NULL;
}
-static int phpdbg_create_recursive_zval_watch(phpdbg_watchpoint_t *watch) {
- HashTable *ht;
- zval *zvp;
+/* ### WATCHING FOR CHANGES ### */
+/* TODO: enforce order: first parents, then children, in order to avoid false positives */
+void phpdbg_check_watchpoint(phpdbg_watchpoint_t *watch) {
+ zend_string *name = NULL;
+ void *comparePtr;
+
+ if (watch->type == WATCH_ON_HASHTABLE) {
+ phpdbg_watch_element *element;
+ zend_string *str;
+ zend_long idx;
+ zval *zv;
+ ZEND_HASH_FOREACH_PTR(&watch->elements, element) {
+ if (element->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_btree_result *res = phpdbg_btree_find(&PHPDBG_G(watch_HashTables), (zend_ulong) HT_WATCH_HT(watch));
+ phpdbg_watch_ht_info *hti = res ? res->ptr : NULL;
+
+ ZEND_HASH_REVERSE_FOREACH_KEY_VAL(HT_WATCH_HT(watch), idx, str, zv) {
+ if (!str) {
+ str = zend_long_to_str(idx); // TODO: hack, use proper int handling for name in parent
+ } else {
+ str = zend_string_copy(str);
+ }
+ if (hti && zend_hash_find(&hti->watches, str)) {
+ zend_string_release(str);
+ break;
+ }
+ ZEND_HASH_FOREACH_PTR(&watch->elements, element) {
+ if (element->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_add_recursive_watch_from_ht(element, idx, str, zv);
+ }
+ } ZEND_HASH_FOREACH_END();
+ phpdbg_notice("watchadd", "element=\"%.*s\"", "Element %.*s has been added to watchpoint", (int) ZSTR_LEN(str), ZSTR_VAL(str));
+ zend_string_release(str);
+ PHPDBG_G(watchpoint_hit) = 1;
+ } ZEND_HASH_FOREACH_END();
+
+ break;
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+ if (watch->type == WATCH_ON_HASHDATA) {
+ return;
+ }
+
+ switch (watch->type) {
+ case WATCH_ON_STR:
+ comparePtr = &ZSTR_LEN(watch->backup.str);
+ break;
+ case WATCH_ON_HASHTABLE:
+ comparePtr = (char *) &watch->backup.ht + HT_WATCH_OFFSET;
+ break;
+ default:
+ comparePtr = &watch->backup;
+ }
+ if (!phpdbg_check_watch_diff(watch->type, comparePtr, watch->addr.ptr)) {
+ return;
+ }
+ if (watch->type == WATCH_ON_REFCOUNTED && !(PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS)) {
+ phpdbg_watch_backup_data(watch);
+ return;
+ }
+ if (watch->type == WATCH_ON_BUCKET) {
+ if (watch->backup.bucket.key != watch->addr.bucket->key || (watch->backup.bucket.key != NULL && watch->backup.bucket.h != watch->addr.bucket->h)) {
+ phpdbg_watch_element *element;
+ zval *new;
- ZEND_ASSERT(watch->type == WATCH_ON_ZVAL);
+ ZEND_HASH_FOREACH_PTR(&watch->elements, element) {
+ break;
+ } ZEND_HASH_FOREACH_END();
- zvp = watch->addr.zv;
- ZVAL_DEREF(zvp);
+ new = zend_symtable_find(element->parent_container, element->name_in_parent);
- if ((ht = HT_FROM_ZVP(zvp))) {
- phpdbg_watchpoint_t *new_watch = emalloc(sizeof(phpdbg_watchpoint_t));
+ if (!new) {
+ /* dequeuing will take care of appropriate notification about removal */
+ phpdbg_remove_watchpoint(watch);
+ return;
+ }
- new_watch->flags = PHPDBG_WATCH_RECURSIVE;
- new_watch->parent = watch;
- new_watch->parent_container = watch->parent_container;
- new_watch->name_in_parent = watch->name_in_parent;
- ++GC_REFCOUNT(new_watch->name_in_parent);
- new_watch->str = strpprintf(0, "%.*s[]", (int) ZSTR_LEN(watch->str), ZSTR_VAL(watch->str));
+ phpdbg_deactivate_watchpoint(watch);
+ phpdbg_remove_watchpoint_btree(watch);
+ watch->addr.zv = new;
+ phpdbg_store_watchpoint_btree(watch);
+ phpdbg_activate_watchpoint(watch);
- if (Z_TYPE_P(zvp) == IS_ARRAY) {
- new_watch->flags |= PHPDBG_WATCH_ARRAY;
- } else {
- new_watch->flags |= PHPDBG_WATCH_OBJECT;
+ if (!phpdbg_check_watch_diff(WATCH_ON_ZVAL, &watch->backup.bucket.val, watch->addr.ptr)) {
+ phpdbg_watch_backup_data(watch);
+ return;
+ }
+ } else if (Z_TYPE_P(watch->addr.zv) == IS_UNDEF) {
+ /* dequeuing will take care of appropriate notification about removal */
+ phpdbg_remove_watchpoint(watch);
+ return;
}
+ }
- phpdbg_create_ht_watchpoint(ht, new_watch);
+ name = phpdbg_watchpoint_change_collision_name(watch);
- phpdbg_create_recursive_ht_watch(new_watch);
+ if (name) {
+ phpdbg_print_watch_diff(watch->type, name, comparePtr, watch->addr.ptr);
+ zend_string_release(name);
+ }
- phpdbg_create_watchpoint(new_watch);
+ if (watch->type == WATCH_ON_ZVAL || watch->type == WATCH_ON_BUCKET) {
+ phpdbg_watch_element *element;
+ phpdbg_update_watch_ref(watch);
+ ZEND_HASH_FOREACH_PTR(&watch->elements, element) {
+ if (element->flags & PHPDBG_WATCH_RECURSIVE) {
+ phpdbg_recurse_watch_element(element);
+ }
+ } ZEND_HASH_FOREACH_END();
}
- return SUCCESS;
+ phpdbg_watch_backup_data(watch);
}
-static void phpdbg_delete_implicit_parents(phpdbg_watchpoint_t *watch) {
- phpdbg_watchpoint_t *parent = watch->parent;
- if (!parent) {
- return;
+void phpdbg_reenable_memory_watches(void) {
+ zend_ulong page;
+ phpdbg_btree_result *res;
+ phpdbg_watchpoint_t *watch;
+
+ ZEND_HASH_FOREACH_NUM_KEY(PHPDBG_G(watchlist_mem), page) {
+ /* Disble writing again if there are any watchers on that page */
+ res = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), page + phpdbg_pagesize - 1);
+ if (res) {
+ watch = res->ptr;
+ if ((char *) page < (char *) watch->addr.ptr + watch->size) {
+ mprotect((void *) page, phpdbg_pagesize, PROT_READ);
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ zend_hash_clean(PHPDBG_G(watchlist_mem));
+}
+
+int phpdbg_print_changed_zvals(void) {
+ int ret;
+ zend_ulong page;
+ phpdbg_watchpoint_t *watch;
+ phpdbg_btree_result *res;
+ HashTable *mem_list = NULL;
+
+ if (zend_hash_num_elements(&PHPDBG_G(watch_elements)) == 0) {
+ return FAILURE;
}
- ZEND_ASSERT(!(parent->flags & PHPDBG_WATCH_RECURSIVE));
- ZEND_ASSERT(parent->flags & PHPDBG_WATCH_IMPLICIT);
+ if (zend_hash_num_elements(PHPDBG_G(watchlist_mem)) > 0) {
+ /* we must not add elements to the hashtable while iterating over it (resize => read into freed memory) */
+ mem_list = PHPDBG_G(watchlist_mem);
+ PHPDBG_G(watchlist_mem) = PHPDBG_G(watchlist_mem_backup);
- if (parent->type == WATCH_ON_HASHTABLE && --parent->implicit_ht_count) {
- return;
+ ZEND_HASH_FOREACH_NUM_KEY(mem_list, page) {
+ phpdbg_btree_position pos = phpdbg_btree_find_between(&PHPDBG_G(watchpoint_tree), page, page + phpdbg_pagesize);
+
+ while ((res = phpdbg_btree_next(&pos))) {
+ watch = res->ptr;
+ phpdbg_check_watchpoint(watch);
+ }
+ if ((res = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), page - 1))) {
+ watch = res->ptr;
+ if ((char *) page < (char *) watch->addr.ptr + watch->size) {
+ phpdbg_check_watchpoint(watch);
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
}
- parent->flags &= ~PHPDBG_WATCH_IMPLICIT;
- if (!(parent->flags & PHPDBG_WATCH_SIMPLE)) {
- if (parent->type == WATCH_ON_ZVAL && Z_REFCOUNTED_P(watch->addr.zv)) {
- phpdbg_remove_watch_collision(parent);
- }
- zend_hash_del(&PHPDBG_G(watchpoints), parent->str);
+ phpdbg_dequeue_elements_for_recreation();
+
+ phpdbg_reenable_memory_watches();
+
+ if (mem_list) {
+ PHPDBG_G(watchlist_mem) = mem_list;
+ phpdbg_reenable_memory_watches();
}
+
+ ret = PHPDBG_G(watchpoint_hit) ? SUCCESS : FAILURE;
+ PHPDBG_G(watchpoint_hit) = 0;
+
+ return ret;
}
-/* delete watchpoint, recursively (and inclusively) */
-static int phpdbg_delete_watchpoint_recursive(phpdbg_watchpoint_t *watch, zend_bool user_request) {
- if (watch->type == WATCH_ON_HASHTABLE) {
- if (user_request) {
- phpdbg_delete_ht_watchpoints_recursive(watch);
- } else {
- HashTable *ht;
- phpdbg_btree_result *result;
+void phpdbg_watch_efree(void *ptr) {
+ phpdbg_btree_result *result;
- ht = HT_FROM_ZVP(watch->addr.zv);
+ /* only do expensive checks if there are any watches at all */
+ if (zend_hash_num_elements(&PHPDBG_G(watch_elements))) {
+ if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) ptr))) {
+ phpdbg_watchpoint_t *watch = result->ptr;
+ if (watch->type != WATCH_ON_HASHDATA) {
+ phpdbg_remove_watchpoint(watch);
+ } else {
+ /* remove all linked watchpoints, they will be dissociated from their elements */
+ phpdbg_watch_element *element;
+ phpdbg_watch_ht_info *hti = (phpdbg_watch_ht_info *) watch;
+
+ ZEND_HASH_FOREACH_PTR(&hti->watches, element) {
+ zend_ulong num = zend_hash_num_elements(&hti->watches);
+ phpdbg_remove_watchpoint(element->watch);
+ if (num == 1) { /* prevent access into freed memory */
+ break;
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+ }
- if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) ht))) {
- phpdbg_delete_watchpoint_recursive((phpdbg_watchpoint_t *) result->ptr, user_request);
+ /* special case watchpoints as they aren't on ptr but on ptr + HT_WATCH_OFFSET */
+ if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), HT_WATCH_OFFSET + (zend_ulong) ptr))) {
+ phpdbg_watchpoint_t *watch = result->ptr;
+ if (watch->type == WATCH_ON_HASHTABLE) {
+ phpdbg_remove_watchpoint(watch);
}
}
- } else if (watch->type == WATCH_ON_ZVAL) {
- phpdbg_delete_zval_watchpoints_recursive(watch);
+
+ zend_hash_index_del(&PHPDBG_G(watch_free), (zend_ulong) ptr);
}
- return zend_hash_del(&PHPDBG_G(watchpoints), watch->str);
+ if (PHPDBG_G(original_free_function)) {
+ PHPDBG_G(original_free_function)(ptr);
+ }
}
-static void phpdbg_delete_ht_watchpoints_recursive(phpdbg_watchpoint_t *watch) {
- zend_string *str, *strkey;
- zend_long numkey;
- phpdbg_watchpoint_t *watchpoint;
-
- ZEND_HASH_FOREACH_KEY(HT_WATCH_HT(watch), numkey, strkey) {
- if (strkey) {
- str = strpprintf(0, "%.*s%s%s%s", (int) ZSTR_LEN(watch->str), ZSTR_VAL(watch->str), (watch->flags & PHPDBG_WATCH_ARRAY) ? "[" : "->", phpdbg_get_property_key(ZSTR_VAL(strkey)), (watch->flags & PHPDBG_WATCH_ARRAY) ? "]" : "");
- } else {
- str = strpprintf(0, "%.*s%s" ZEND_LONG_FMT "%s", (int) ZSTR_LEN(watch->str), ZSTR_VAL(watch->str), (watch->flags & PHPDBG_WATCH_ARRAY) ? "[" : "->", numkey, (watch->flags & PHPDBG_WATCH_ARRAY) ? "]" : "");
- }
+/* ### USER API ### */
+void phpdbg_list_watchpoints(void) {
+ phpdbg_watch_element *element;
- if ((watchpoint = zend_hash_find_ptr(&PHPDBG_G(watchpoints), str))) {
- phpdbg_delete_watchpoint_recursive(watchpoint, 1);
- }
+ phpdbg_xml("<watchlist %r>");
- zend_string_release(str);
+ ZEND_HASH_FOREACH_PTR(&PHPDBG_G(watch_elements), element) {
+ phpdbg_writeln("watchvariable", "variable=\"%.*s\" on=\"%s\" type=\"%s\"", "%.*s (%s, %s)", (int) ZSTR_LEN(element->str), ZSTR_VAL(element->str), (element->flags & (PHPDBG_WATCH_ARRAY|PHPDBG_WATCH_OBJECT)) ? "array" : "variable", (element->flags & PHPDBG_WATCH_RECURSIVE) ? "recursive" : "simple");
} ZEND_HASH_FOREACH_END();
-}
-static void phpdbg_delete_zval_watchpoints_recursive(phpdbg_watchpoint_t *watch) {
- if (Z_REFCOUNTED_P(watch->addr.zv)) {
- phpdbg_remove_watch_collision(watch);
- }
+ phpdbg_xml("</watchlist>");
}
-/* delete watchpoints recusively (exclusively!) */
-static void phpdbg_delete_watchpoints_recursive(phpdbg_watchpoint_t *watch) {
- if (watch->type == WATCH_ON_ZVAL) {
- phpdbg_delete_zval_watchpoints_recursive(watch);
- } else if (watch->type == WATCH_ON_HASHTABLE) {
- phpdbg_delete_ht_watchpoints_recursive(watch);
- }
+static int phpdbg_create_simple_watchpoint(zval *zv, phpdbg_watch_element *element) {
+ element->flags = PHPDBG_WATCH_SIMPLE;
+ phpdbg_add_bucket_watch_element((Bucket *) zv, element);
+ return SUCCESS;
}
-static int phpdbg_delete_watchpoint(phpdbg_watchpoint_t *tmp_watch) {
- int ret;
- phpdbg_watchpoint_t *watch;
- phpdbg_btree_result *result;
+static int phpdbg_create_array_watchpoint(zval *zv, phpdbg_watch_element *element) {
+ phpdbg_watch_element *new;
+ zend_string *str;
+ zval *orig_zv = zv;
- if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) tmp_watch->addr.ptr)) == NULL) {
+ ZVAL_DEREF(zv);
+ if (Z_TYPE_P(zv) != IS_ARRAY && Z_TYPE_P(zv) != IS_OBJECT) {
return FAILURE;
}
- watch = result->ptr;
-
- if (!(watch->flags & PHPDBG_WATCH_NORMAL) || (watch->parent && (watch->parent->flags & PHPDBG_WATCH_RECURSIVE))) {
- return FAILURE; /* TODO: better error message for recursive (??) */
- }
+ new = ecalloc(1, sizeof(phpdbg_watch_element));
- if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
- ret = phpdbg_delete_watchpoint_recursive(watch, 1);
- } else {
- ret = zend_hash_del(&PHPDBG_G(watchpoints), watch->str);
- }
+ str = strpprintf(0, "%.*s[]", (int) ZSTR_LEN(element->str), ZSTR_VAL(element->str));
+ zend_string_release(element->str);
+ element->str = str;
+ element->flags = PHPDBG_WATCH_IMPLICIT;
+ phpdbg_add_bucket_watch_element((Bucket *) orig_zv, element);
+ element->child = new;
- phpdbg_free_watch(tmp_watch);
- efree(tmp_watch);
+ new->flags = PHPDBG_WATCH_SIMPLE;
+ new->str = zend_string_copy(str);
+ new->parent = element;
+ phpdbg_add_ht_watch_element(zv, new);
+ return SUCCESS;
+}
- return ret;
+static int phpdbg_create_recursive_watchpoint(zval *zv, phpdbg_watch_element *element) {
+ element->flags = PHPDBG_WATCH_RECURSIVE | PHPDBG_WATCH_RECURSIVE_ROOT;
+ element->child = NULL;
+ phpdbg_add_bucket_watch_element((Bucket *) zv, element);
+ return SUCCESS;
}
-static int phpdbg_watchpoint_parse_wrapper(char *name, size_t namelen, char *key, size_t keylen, HashTable *parent, zval *zv, int (*callback)(phpdbg_watchpoint_t *)) {
+typedef struct { int (*callback)(zval *zv, phpdbg_watch_element *); zend_string *str; } phpdbg_watch_parse_struct;
+
+static int phpdbg_watchpoint_parse_wrapper(char *name, size_t namelen, char *key, size_t keylen, HashTable *parent, zval *zv, phpdbg_watch_parse_struct *info) {
int ret;
- phpdbg_watchpoint_t *watch = ecalloc(1, sizeof(phpdbg_watchpoint_t));
- watch->str = zend_string_init(name, namelen, 0);
- watch->name_in_parent = zend_string_init(key, keylen, 0);
- watch->parent_container = parent;
- phpdbg_create_zval_watchpoint(zv, watch);
+ phpdbg_watch_element *element = ecalloc(1, sizeof(phpdbg_watch_element));
+ element->str = zend_string_init(name, namelen, 0);
+ element->name_in_parent = zend_string_init(key, keylen, 0);
+ element->parent_container = parent;
+ element->parent = PHPDBG_G(watch_tmp);
+ element->child = NULL;
- ret = callback(watch);
+ ret = info->callback(zv, element);
efree(name);
efree(key);
if (ret != SUCCESS) {
- phpdbg_free_watch(watch);
- efree(watch);
+ phpdbg_remove_watch_element(element);
+ } else {
+ if (PHPDBG_G(watch_tmp)) {
+ PHPDBG_G(watch_tmp)->child = element;
+ }
+
+ if (element->child) {
+ element = element->child;
+ }
+ element->id = PHPDBG_G(watch_elements).nNextFreeElement;
+ zend_hash_index_add_ptr(&PHPDBG_G(watch_elements), element->id, element);
+
+ phpdbg_notice("watchadd", "index=\"%d\" variable=\"%.*s\"", "Added%s watchpoint #%d for %.*s", (element->flags & PHPDBG_WATCH_RECURSIVE_ROOT) ? " recursive" : "", element->id, (int) ZSTR_LEN(element->str), ZSTR_VAL(element->str));
}
PHPDBG_G(watch_tmp) = NULL;
@@ -649,56 +1259,76 @@ static int phpdbg_watchpoint_parse_wrapper(char *name, size_t namelen, char *key
return ret;
}
-PHPDBG_API int phpdbg_watchpoint_parse_input(char *input, size_t len, HashTable *parent, size_t i, int (*callback)(phpdbg_watchpoint_t *), zend_bool silent) {
- return phpdbg_parse_variable_with_arg(input, len, parent, i, (phpdbg_parse_var_with_arg_func) phpdbg_watchpoint_parse_wrapper, NULL, 0, callback);
+PHPDBG_API int phpdbg_watchpoint_parse_input(char *input, size_t len, HashTable *parent, size_t i, phpdbg_watch_parse_struct *info, zend_bool silent) {
+ return phpdbg_parse_variable_with_arg(input, len, parent, i, (phpdbg_parse_var_with_arg_func) phpdbg_watchpoint_parse_wrapper, NULL, 0, info);
}
-static int phpdbg_watchpoint_parse_step(char *name, size_t namelen, char *key, size_t keylen, HashTable *parent, zval *zv, int (*callback)(phpdbg_watchpoint_t *)) {
- phpdbg_watchpoint_t *watch;
+static int phpdbg_watchpoint_parse_step(char *name, size_t namelen, char *key, size_t keylen, HashTable *parent, zval *zv, phpdbg_watch_parse_struct *info) {
+ phpdbg_watch_element *element;
- if ((watch = zend_hash_str_find_ptr(&PHPDBG_G(watchpoints), name, namelen))) {
- watch->flags |= PHPDBG_WATCH_IMPLICIT;
- PHPDBG_G(watch_tmp) = watch;
+ /* do not install watch elements for references */
+ if (PHPDBG_G(watch_tmp) && Z_ISREF_P(PHPDBG_G(watch_tmp)->watch->addr.zv) && Z_REFVAL_P(PHPDBG_G(watch_tmp)->watch->addr.zv) == zv) {
+ efree(name);
+ efree(key);
return SUCCESS;
}
- watch = ecalloc(1, sizeof(phpdbg_watchpoint_t));
- watch->flags = PHPDBG_WATCH_IMPLICIT;
- watch->str = zend_string_init(name, namelen, 0);
- watch->name_in_parent = zend_string_init(key, keylen, 0);
- watch->parent_container = parent;
- watch->parent = PHPDBG_G(watch_tmp);
- phpdbg_create_zval_watchpoint(zv, watch);
-
- phpdbg_create_watchpoint(watch);
+ element = ecalloc(1, sizeof(phpdbg_watch_element));
+ element->flags = PHPDBG_WATCH_IMPLICIT;
+ element->str = zend_string_copy(info->str);
+ element->name_in_parent = zend_string_init(key, keylen, 0);
+ element->parent_container = parent;
+ element->parent = PHPDBG_G(watch_tmp);
+ element = phpdbg_add_bucket_watch_element((Bucket *) zv, element);
efree(name);
efree(key);
- PHPDBG_G(watch_tmp) = watch;
+ if (PHPDBG_G(watch_tmp)) {
+ PHPDBG_G(watch_tmp)->child = element;
+ }
+ PHPDBG_G(watch_tmp) = element;
+
return SUCCESS;
}
-static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, int (*callback)(phpdbg_watchpoint_t *)) {
- if (EG(scope) && len >= 5 && !memcmp("$this", input, 5)) {
+static int phpdbg_watchpoint_parse_symtables(char *input, size_t len, int (*callback)(zval *, phpdbg_watch_element *)) {
+ zend_class_entry *scope = zend_get_executed_scope();
+ phpdbg_watch_parse_struct info;
+ int ret;
+
+ if (scope && len >= 5 && !memcmp("$this", input, 5)) {
zend_hash_str_add(EG(current_execute_data)->symbol_table, ZEND_STRL("this"), &EG(current_execute_data)->This);
}
- if (phpdbg_is_auto_global(input, len) && phpdbg_watchpoint_parse_input(input, len, &EG(symbol_table), 0, callback, 1) != FAILURE) {
+ if (callback == phpdbg_create_array_watchpoint) {
+ info.str = strpprintf(0, "%.*s[]", (int) len, input);
+ } else {
+ info.str = zend_string_init(input, len, 0);
+ }
+ info.callback = callback;
+
+ if (phpdbg_is_auto_global(input, len) && phpdbg_watchpoint_parse_input(input, len, &EG(symbol_table), 0, &info, 1) != FAILURE) {
+ zend_string_release(info.str);
return SUCCESS;
}
- return phpdbg_parse_variable_with_arg(input, len, EG(current_execute_data)->symbol_table, 0, (phpdbg_parse_var_with_arg_func) phpdbg_watchpoint_parse_wrapper, (phpdbg_parse_var_with_arg_func) phpdbg_watchpoint_parse_step, 0, callback);
+ ret = phpdbg_parse_variable_with_arg(input, len, EG(current_execute_data)->symbol_table, 0, (phpdbg_parse_var_with_arg_func) phpdbg_watchpoint_parse_wrapper, (phpdbg_parse_var_with_arg_func) phpdbg_watchpoint_parse_step, 0, &info);
+
+ zend_string_release(info.str);
+ return ret;
}
PHPDBG_WATCH(delete) /* {{{ */
{
+ phpdbg_watch_element *element;
switch (param->type) {
- case STR_PARAM:
- if (phpdbg_delete_var_watchpoint(param->str, param->len) == FAILURE) {
- phpdbg_error("watchdelete", "type=\"nowatch\"", "Nothing was deleted, no corresponding watchpoint found");
+ case NUMERIC_PARAM:
+ if ((element = zend_hash_index_find_ptr(&PHPDBG_G(watch_elements), param->num))) {
+ phpdbg_remove_watch_element(element);
+ phpdbg_notice("watchdelete", "variable=\"%.*s\"", "Removed watchpoint %d", (int) param->num);
} else {
- phpdbg_notice("watchdelete", "variable=\"%.*s\"", "Removed watchpoint %.*s", (int) param->len, param->str);
+ phpdbg_error("watchdelete", "type=\"nowatch\"", "Nothing was deleted, no corresponding watchpoint found");
}
break;
@@ -708,6 +1338,14 @@ PHPDBG_WATCH(delete) /* {{{ */
return SUCCESS;
} /* }}} */
+int phpdbg_create_var_watchpoint(char *input, size_t len) {
+ if (phpdbg_rebuild_symtable() == FAILURE) {
+ return FAILURE;
+ }
+
+ return phpdbg_watchpoint_parse_symtables(input, len, phpdbg_create_simple_watchpoint);
+}
+
PHPDBG_WATCH(recursive) /* {{{ */
{
if (phpdbg_rebuild_symtable() == FAILURE) {
@@ -716,9 +1354,7 @@ PHPDBG_WATCH(recursive) /* {{{ */
switch (param->type) {
case STR_PARAM:
- if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_recursive_watchpoint) != FAILURE) {
- phpdbg_notice("watchrecursive", "variable=\"%.*s\"", "Set recursive watchpoint on %.*s", (int)param->len, param->str);
- }
+ phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_recursive_watchpoint);
break;
phpdbg_default_switch_case();
@@ -735,9 +1371,7 @@ PHPDBG_WATCH(array) /* {{{ */
switch (param->type) {
case STR_PARAM:
- if (phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_array_watchpoint) != FAILURE) {
- phpdbg_notice("watcharray", "variable=\"%.*s\"", "Set array watchpoint on %.*s", (int)param->len, param->str);
- }
+ phpdbg_watchpoint_parse_symtables(param->str, param->len, phpdbg_create_array_watchpoint);
break;
phpdbg_default_switch_case();
@@ -746,148 +1380,6 @@ PHPDBG_WATCH(array) /* {{{ */
return SUCCESS;
} /* }}} */
-void phpdbg_watch_HashTable_dtor(zval *zv) {
- phpdbg_btree_result *result;
- zval *orig_zv = zv;
-
- while (Z_TYPE_P(zv) == IS_INDIRECT) {
- zv = Z_INDIRECT_P(zv);
- }
-
- if ((result = phpdbg_btree_find(&PHPDBG_G(watchpoint_tree), (zend_ulong) zv))) {
- phpdbg_watchpoint_t *watch = result->ptr;
-
- if (watch->flags & PHPDBG_WATCH_NORMAL) {
- PHPDBG_G(watchpoint_hit) = 1;
-
- phpdbg_notice("watchdelete", "variable=\"%.*s\" recursive=\"%s\"", "%.*s was removed, removing watchpoint%s", (int) ZSTR_LEN(watch->str), ZSTR_VAL(watch->str), (watch->flags & PHPDBG_WATCH_RECURSIVE) ? " recursively" : "");
- }
-
- if ((result = phpdbg_btree_find(&PHPDBG_G(watch_HashTables), (zend_ulong) watch->parent_container))) {
- phpdbg_watch_ht_info *hti = result->ptr;
- hti->dtor(orig_zv);
- zend_hash_del(&hti->watches, watch->name_in_parent);
- if (zend_hash_num_elements(&hti->watches) == 0) {
- watch->parent_container->pDestructor = hti->dtor;
- zend_hash_destroy(&hti->watches);
- phpdbg_btree_delete(&PHPDBG_G(watch_HashTables), (zend_ulong) watch->parent_container);
- efree(hti);
- }
- } else {
- zval_ptr_dtor_wrapper(orig_zv);
- }
-
- if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
- phpdbg_delete_watchpoint_recursive(watch, 0);
- } else {
- zend_hash_del(&PHPDBG_G(watchpoints), watch->str);
- }
- }
-}
-
-int phpdbg_create_var_watchpoint(char *input, size_t len) {
- if (phpdbg_rebuild_symtable() == FAILURE) {
- return FAILURE;
- }
-
- return phpdbg_watchpoint_parse_symtables(input, len, phpdbg_create_simple_watchpoint);
-}
-
-int phpdbg_delete_var_watchpoint(char *input, size_t len) {
- if (phpdbg_rebuild_symtable() == FAILURE) {
- return FAILURE;
- }
-
- return phpdbg_watchpoint_parse_input(input, len, EG(current_execute_data)->symbol_table, 0, phpdbg_delete_watchpoint, 0);
-}
-
-#ifdef _WIN32
-int phpdbg_watchpoint_segfault_handler(void *addr) {
-#else
-int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context) {
-#endif
- void *page;
- phpdbg_watch_memdump *dump;
- phpdbg_watchpoint_t *watch;
- size_t size;
-
- watch = phpdbg_check_for_watchpoint(
-#ifdef _WIN32
- addr
-#else
- info->si_addr
-#endif
- );
-
- if (watch == NULL) {
- return FAILURE;
- }
-
- page = phpdbg_get_page_boundary(watch->addr.ptr);
- size = phpdbg_get_total_page_size(watch->addr.ptr, watch->size);
-
- /* re-enable writing */
- mprotect(page, size, PROT_READ | PROT_WRITE);
-
- dump = malloc(MEMDUMP_SIZE(size));
- dump->page = page;
- dump->size = size;
- dump->reenable_writing = 0;
-
- memcpy(&dump->data, page, size);
-
- zend_llist_add_element(&PHPDBG_G(watchlist_mem), &dump);
-
- return SUCCESS;
-}
-
-void phpdbg_watchpoints_clean(void) {
- zend_hash_clean(&PHPDBG_G(watchpoints));
-}
-
-/* due to implicit delete... MUST BE DESTROYED MANUALLY */
-static void phpdbg_watch_dtor(zval *pDest) {
- phpdbg_watchpoint_t *watch = (phpdbg_watchpoint_t *) Z_PTR_P(pDest);
-
- if (watch->flags & PHPDBG_WATCH_IMPLICIT) {
- watch->flags = PHPDBG_WATCH_SIMPLE; // tiny hack for delete_implicit_parents
-
- if (watch->type == WATCH_ON_ZVAL) {
- phpdbg_delete_zval_watchpoints_recursive(watch);
- } else if (watch->type == WATCH_ON_HASHTABLE) {
- phpdbg_watchpoint_t *watchpoint;
-
- watch->implicit_ht_count++;
-
- ZEND_HASH_FOREACH_PTR(&((phpdbg_watch_ht_info *) phpdbg_btree_find(&PHPDBG_G(watch_HashTables), (zend_ulong) HT_WATCH_HT(watch))->ptr)->watches, watchpoint) {
- phpdbg_delete_watchpoint_recursive(watchpoint, 1);
- } ZEND_HASH_FOREACH_END();
- }
- }
-
- phpdbg_delete_implicit_parents(watch);
-
- phpdbg_deactivate_watchpoint(watch);
- phpdbg_remove_watchpoint(watch);
-
- phpdbg_free_watch(watch);
- efree(watch);
-}
-
-static void phpdbg_watch_mem_dtor(void *llist_data) {
- phpdbg_watch_memdump *dump = *(phpdbg_watch_memdump **) llist_data;
-
- /* Disble writing again */
- if (dump->reenable_writing) {
- mprotect(dump->page, dump->size, PROT_READ);
- }
-
- free(dump);
-}
-
-static void phpdbg_watch_free_ptr_dtor(zval *ptr) {
- efree(Z_PTR_P(ptr));
-}
void phpdbg_setup_watchpoints(void) {
#if _SC_PAGE_SIZE
@@ -900,267 +1392,42 @@ void phpdbg_setup_watchpoints(void) {
phpdbg_pagesize = 4096; /* common pagesize */
#endif
- zend_llist_init(&PHPDBG_G(watchlist_mem), sizeof(void *), phpdbg_watch_mem_dtor, 1);
phpdbg_btree_init(&PHPDBG_G(watchpoint_tree), sizeof(void *) * 8);
phpdbg_btree_init(&PHPDBG_G(watch_HashTables), sizeof(void *) * 8);
- zend_hash_init(&PHPDBG_G(watchpoints), 8, NULL, phpdbg_watch_dtor, 0);
- zend_hash_init(&PHPDBG_G(watch_collisions), 8, NULL, phpdbg_watch_free_ptr_dtor, 0);
-}
-
-static void phpdbg_print_changed_zval(phpdbg_watch_memdump *dump) {
- /* fetch all changes between dump->page and dump->page + dump->size */
- phpdbg_btree_position pos = phpdbg_btree_find_between(&PHPDBG_G(watchpoint_tree), (zend_ulong) dump->page, (zend_ulong) dump->page + dump->size);
- phpdbg_btree_result *result;
- int elementDiff;
- void *curTest;
-
- dump->reenable_writing = 0;
-
- while ((result = phpdbg_btree_next(&pos))) {
- phpdbg_watchpoint_t *watch = result->ptr;
- void *oldPtr = (char *) &dump->data + ((size_t) watch->addr.ptr - (size_t) dump->page);
- char reenable = 1;
- int removed = 0;
-
- if ((size_t) watch->addr.ptr < (size_t) dump->page || (size_t) watch->addr.ptr + watch->size > (size_t) dump->page + dump->size) {
- continue;
- }
-
- /* Test if the zval was separated or replaced and if necessary move the watchpoint */
- if ((watch->type == WATCH_ON_HASHTABLE || watch->type == WATCH_ON_ZVAL) && watch->parent_container) {
- if ((curTest = zend_symtable_find(watch->parent_container, watch->name_in_parent))) {
- while (Z_TYPE_P((zval *) curTest) == IS_INDIRECT) {
- curTest = Z_INDIRECT_P((zval *) curTest);
- }
-
- if (watch->type == WATCH_ON_HASHTABLE) {
- switch (Z_TYPE_P((zval *) curTest)) {
- case IS_ARRAY:
- curTest = (void *) Z_ARRVAL_P((zval *) curTest);
- break;
- case IS_OBJECT:
- curTest = (void *) Z_OBJPROP_P((zval *) curTest);
- break;
- }
- }
-
- if (curTest != watch->addr.ptr) {
- phpdbg_deactivate_watchpoint(watch);
- phpdbg_remove_watchpoint(watch);
- watch->addr.ptr = curTest;
- phpdbg_store_watchpoint(watch);
- phpdbg_activate_watchpoint(watch);
-
- reenable = 0;
- }
- } else {
- removed = 1;
- }
- }
-
- /* Show to the user what changed and delete watchpoint upon removal */
- {
- zend_bool do_break = 0;
-
- switch (watch->type) {
- case WATCH_ON_ZVAL:
- do_break = memcmp(oldPtr, watch->addr.zv, sizeof(zend_value) + sizeof(uint32_t) /* value + typeinfo */) != 0;
- if (!do_break) {
- goto end;
- }
- break;
- case WATCH_ON_HASHTABLE:
- do_break = zend_hash_num_elements(HT_PTR_HT(oldPtr)) != zend_hash_num_elements(HT_WATCH_HT(watch));
- if (!do_break) {
- goto end;
- }
- break;
- case WATCH_ON_REFCOUNTED:
- do_break = memcmp(oldPtr, watch->addr.ref, sizeof(uint32_t) /* no zend_refcounted metadata info */) != 0;
- if (!do_break) {
- goto end;
- }
- if (!(PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS)) {
- do_break = 0;
- }
- break;
- }
-
- if (!(watch->flags & PHPDBG_WATCH_NORMAL)) {
- do_break = 0;
- }
-
- if (do_break) {
- PHPDBG_G(watchpoint_hit) = 1;
-
- phpdbg_notice("watchhit", "variable=\"%s\"", "Breaking on watchpoint %.*s", (int) ZSTR_LEN(watch->str), ZSTR_VAL(watch->str));
- phpdbg_xml("<watchdata %r>");
- }
-
- switch (watch->type) {
- case WATCH_ON_ZVAL: {
- zend_bool show_value = memcmp(oldPtr, watch->addr.zv, sizeof(zend_value) + sizeof(uint32_t) /* no metadata info */) != 0;
-
- if ((watch->flags & PHPDBG_WATCH_NORMAL) && (removed || show_value)) {
-/* TODO: Merge with refcounting watches, store if watched ref value is to be dropped etc. [for example: manually increment refcount transparently for displaying and drop it if it decrements to 1] */
- if (Z_REFCOUNTED_P((zval *) oldPtr)) {
- phpdbg_writeln("watchvalue", "type=\"old\" inaccessible=\"inaccessible\"", "Old value inaccessible or destroyed");
- } else {
- phpdbg_out("Old value: ");
- phpdbg_xml("<watchvalue %r type=\"old\">");
- zend_print_flat_zval_r((zval *) oldPtr);
- phpdbg_xml("</watchvalue>");
- phpdbg_out("\n");
- }
- }
-
- /* check if zval was removed */
- if (removed) {
- if (watch->flags & PHPDBG_WATCH_NORMAL) {
- phpdbg_notice("watchdelete", "variable=\"%.*s\"", "Watchpoint %.*s was unset, removing watchpoint", (int) ZSTR_LEN(watch->str), ZSTR_VAL(watch->str));
- }
- zend_hash_del(&PHPDBG_G(watchpoints), watch->str);
-
- reenable = 0;
-
- if (Z_REFCOUNTED_P((zval *) oldPtr)) {
- phpdbg_remove_watch_collision(watch);
- }
- break;
- }
-
- if ((watch->flags & PHPDBG_WATCH_NORMAL) && show_value) {
- phpdbg_out("New value%s: ", Z_ISREF_P(watch->addr.zv) ? " (reference)" : "");
- phpdbg_xml("<watchvalue %r%s type=\"new\">", Z_ISREF_P(watch->addr.zv) ? " reference=\"reference\"" : "");
- zend_print_flat_zval_r(watch->addr.zv);
- phpdbg_xml("</watchvalue>");
- phpdbg_out("\n");
- }
-
- /* add new watchpoints if necessary */
- if (Z_PTR_P(watch->addr.zv) != Z_PTR_P((zval *) oldPtr) || Z_TYPE_P(watch->addr.zv) != Z_TYPE_P((zval *) oldPtr)) {
- if (Z_REFCOUNTED_P((zval *) oldPtr)) {
- zval *new_zv = watch->addr.zv;
- watch->addr.ptr = oldPtr;
- phpdbg_remove_watch_collision(watch);
- watch->addr.zv = new_zv;
- }
- if (Z_REFCOUNTED_P(watch->addr.zv)) {
- if ((watch->flags & PHPDBG_WATCH_NORMAL) && (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS)) {
- phpdbg_writeln("watchrefcount", "type=\"new\" refcount=\"%d\"", "New refcount: %d", Z_REFCOUNT_P(watch->addr.zv));
- }
- if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
- phpdbg_create_recursive_watchpoint(watch);
- } else if (Z_ISREF_P(watch->addr.zv)) {
- phpdbg_create_reference_watch(watch);
- }
- }
- }
-
- break;
- }
- case WATCH_ON_HASHTABLE:
- /* We should be safely able to assume the HashTable to be consistent (inconsistent HashTables should have been caught by phpdbg_watch_efree() */
- elementDiff = zend_hash_num_elements(HT_PTR_HT(oldPtr)) - zend_hash_num_elements(HT_WATCH_HT(watch));
- if ((watch->flags & PHPDBG_WATCH_NORMAL) && elementDiff) {
- if (elementDiff > 0) {
- phpdbg_writeln("watchsize", "removed=\"%d\"", "%d elements were removed from the array", elementDiff);
- } else {
- phpdbg_writeln("watchsize", "added=\"%d\"", "%d elements were added to the array", -elementDiff);
- }
- }
- /* add new watchpoints if necessary */
- if (watch->flags & PHPDBG_WATCH_RECURSIVE) {
- phpdbg_create_recursive_ht_watch(watch);
- }
- if ((watch->flags & PHPDBG_WATCH_NORMAL) && HT_WATCH_HT(watch)->nInternalPointer != HT_PTR_HT(oldPtr)->nInternalPointer) {
- phpdbg_writeln("watcharrayptr", "", "Internal pointer of array was changed");
- }
- break;
- case WATCH_ON_REFCOUNTED: {
- if ((watch->flags & PHPDBG_WATCH_NORMAL) && (PHPDBG_G(flags) & PHPDBG_SHOW_REFCOUNTS)) {
- phpdbg_writeln("watchrefcount", "type=\"old\" refcount=\"%d\"", "Old refcount: %d", GC_REFCOUNT((zend_refcounted *) oldPtr));
- phpdbg_writeln("watchrefcount", "type=\"new\" refcount=\"%d\"", "New refcount: %d", GC_REFCOUNT(watch->addr.ref));
- }
- break;
- }
- }
-
- if (do_break) {
- phpdbg_xml("</watchdata>");
- }
- } end:
-
- dump->reenable_writing = dump->reenable_writing | reenable;
- }
-}
-
-int phpdbg_print_changed_zvals(void) {
- zend_llist_position pos;
- phpdbg_watch_memdump **dump;
- int ret;
-
- if (zend_llist_count(&PHPDBG_G(watchlist_mem)) == 0) {
- return FAILURE;
- }
-
- dump = (phpdbg_watch_memdump **) zend_llist_get_last_ex(&PHPDBG_G(watchlist_mem), &pos);
-
- do {
- phpdbg_print_changed_zval(*dump);
- } while ((dump = (phpdbg_watch_memdump **) zend_llist_get_prev_ex(&PHPDBG_G(watchlist_mem), &pos)));
-
- zend_llist_clean(&PHPDBG_G(watchlist_mem));
-
- ret = PHPDBG_G(watchpoint_hit) ? SUCCESS : FAILURE;
- PHPDBG_G(watchpoint_hit) = 0;
-
- return ret;
+ zend_hash_init(&PHPDBG_G(watch_elements), 8, NULL, NULL, 0);
+ zend_hash_init(&PHPDBG_G(watch_collisions), 8, NULL, NULL, 0);
+ zend_hash_init(&PHPDBG_G(watch_recreation), 8, NULL, NULL, 0);
+ zend_hash_init(&PHPDBG_G(watch_free), 8, NULL, NULL, 0);
+
+ /* put these on a separate page, to avoid conflicts with other memory */
+ PHPDBG_G(watchlist_mem) = malloc(phpdbg_pagesize > sizeof(HashTable) ? phpdbg_pagesize : sizeof(HashTable));
+ zend_hash_init(PHPDBG_G(watchlist_mem), phpdbg_pagesize / (sizeof(Bucket) + sizeof(uint32_t)), NULL, NULL, 1);
+ PHPDBG_G(watchlist_mem_backup) = malloc(phpdbg_pagesize > sizeof(HashTable) ? phpdbg_pagesize : sizeof(HashTable));
+ zend_hash_init(PHPDBG_G(watchlist_mem_backup), phpdbg_pagesize / (sizeof(Bucket) + sizeof(uint32_t)), NULL, NULL, 1);
}
-void phpdbg_list_watchpoints(void) {
- phpdbg_watchpoint_t *watch;
-
- phpdbg_xml("<watchlist %r>");
+void phpdbg_destroy_watchpoints(void) {
+ phpdbg_watch_element *element;
+ phpdbg_btree_position pos;
+ phpdbg_btree_result *res;
- ZEND_HASH_FOREACH_PTR(&PHPDBG_G(watchpoints), watch) {
- if (watch->flags & PHPDBG_WATCH_NORMAL) {
- phpdbg_writeln("watchvariable", "variable=\"%.*s\" on=\"%s\" type=\"%s\"", "%.*s (%s, %s)", (int) ZSTR_LEN(watch->str), ZSTR_VAL(watch->str), watch->type == WATCH_ON_HASHTABLE ? "array" : watch->type == WATCH_ON_REFCOUNTED ? "refcount" : "variable", watch->flags == PHPDBG_WATCH_RECURSIVE ? "recursive" : "simple");
- }
+ /* unconditionally free all remaining elements to avoid memory leaks */
+ ZEND_HASH_FOREACH_PTR(&PHPDBG_G(watch_recreation), element) {
+ phpdbg_automatic_dequeue_free(element);
} ZEND_HASH_FOREACH_END();
- phpdbg_xml("</watchlist>");
-}
-
-void phpdbg_watch_efree(void *ptr) {
- phpdbg_btree_result *result;
-
- result = phpdbg_btree_find_closest(&PHPDBG_G(watchpoint_tree), (zend_ulong) ptr);
-
- if (result) {
- phpdbg_watchpoint_t *watch = result->ptr;
-
- if ((size_t) watch->addr.ptr + watch->size > (size_t) ptr) {
- if (watch->type == WATCH_ON_REFCOUNTED) {
- /* remove watchpoint here from btree, zval watchpoint will remove it via remove_watch_collison */
- phpdbg_deactivate_watchpoint(watch);
- phpdbg_remove_watchpoint(watch);
- } else {
- if (watch->type == WATCH_ON_ZVAL) {
- phpdbg_remove_watch_collision(watch);
- }
- if (watch->type == WATCH_ON_HASHTABLE && (watch->flags & PHPDBG_WATCH_SIMPLE)) {
- /* when a HashTable is freed, we can safely assume the other zvals all were dtor'ed */
- phpdbg_notice("watchdelete", "variable=\"%.*s\" recursive=\"%s\"", "Array %.*s was removed, removing watchpoint%s", (int) ZSTR_LEN(watch->str), ZSTR_VAL(watch->str), (watch->flags & PHPDBG_WATCH_RECURSIVE) ? " recursively" : "");
- }
- if (watch->type == WATCH_ON_HASHTABLE || watch->parent == NULL || watch->parent->type != WATCH_ON_ZVAL) { /* no references */
- zend_hash_del(&PHPDBG_G(watchpoints), watch->str);
- }
- }
- }
+ /* upon fatal errors etc. (i.e. CG(unclean_shutdown) == 1), some watchpoints may still be active. Ensure memory is not watched anymore for next run. Do not care about memory freeing here, shutdown is unclean and near anyway. */
+ pos = phpdbg_btree_find_between(&PHPDBG_G(watchpoint_tree), 0, -1);
+ while ((res = phpdbg_btree_next(&pos))) {
+ phpdbg_deactivate_watchpoint(res->ptr);
}
- if (PHPDBG_G(original_free_function)) {
- PHPDBG_G(original_free_function)(ptr);
- }
+ zend_hash_destroy(&PHPDBG_G(watch_elements)); PHPDBG_G(watch_elements).nNumOfElements = 0; /* phpdbg_watch_efree() is checking against this arrays size */
+ zend_hash_destroy(&PHPDBG_G(watch_recreation));
+ zend_hash_destroy(&PHPDBG_G(watch_free));
+ zend_hash_destroy(&PHPDBG_G(watch_collisions));
+ zend_hash_destroy(PHPDBG_G(watchlist_mem));
+ free(PHPDBG_G(watchlist_mem));
+ zend_hash_destroy(PHPDBG_G(watchlist_mem_backup));
+ free(PHPDBG_G(watchlist_mem_backup));
}
diff --git a/sapi/phpdbg/phpdbg_watch.h b/sapi/phpdbg/phpdbg_watch.h
index 0d5c5ff25f..497776081f 100644
--- a/sapi/phpdbg/phpdbg_watch.h
+++ b/sapi/phpdbg/phpdbg_watch.h
@@ -45,6 +45,9 @@ typedef enum {
WATCH_ON_ZVAL,
WATCH_ON_HASHTABLE,
WATCH_ON_REFCOUNTED,
+ WATCH_ON_STR,
+ WATCH_ON_HASHDATA,
+ WATCH_ON_BUCKET,
} phpdbg_watchtype;
@@ -54,41 +57,68 @@ typedef enum {
#define PHPDBG_WATCH_OBJECT 0x08
#define PHPDBG_WATCH_NORMAL (PHPDBG_WATCH_SIMPLE | PHPDBG_WATCH_RECURSIVE)
#define PHPDBG_WATCH_IMPLICIT 0x10
+#define PHPDBG_WATCH_RECURSIVE_ROOT 0x20
-#define PHPDBG_DESTRUCTED_ZVAL 0x80
+typedef struct _phpdbg_watch_collision phpdbg_watch_collision;
-typedef struct _phpdbg_watchpoint_t phpdbg_watchpoint_t;
-
-struct _phpdbg_watchpoint_t {
+typedef struct _phpdbg_watchpoint_t {
union {
zval *zv;
zend_refcounted *ref;
+ Bucket *bucket;
void *ptr;
} addr;
size_t size;
phpdbg_watchtype type;
- char flags;
- unsigned int implicit_ht_count;
- phpdbg_watchpoint_t *parent;
- phpdbg_watchpoint_t *reference;
- HashTable *parent_container;
- zend_string *name_in_parent;
- zend_string *str;
+ zend_refcounted *ref; /* key to fetch the collision on parents */
+ HashTable elements;
+ phpdbg_watch_collision *coll; /* only present on *children* */
+ union {
+ zval zv;
+ Bucket bucket;
+ zend_refcounted ref;
+ HashTable ht;
+ zend_string *str;
+ } backup;
+} phpdbg_watchpoint_t;
+
+struct _phpdbg_watch_collision {
+ phpdbg_watchpoint_t ref;
+ phpdbg_watchpoint_t reference;
+ HashTable parents;
};
-typedef struct {
+typedef struct _phpdbg_watch_element {
+ uint32_t id;
phpdbg_watchpoint_t *watch;
- unsigned int refs;
- HashTable watches;
- HashTable implicit_watches;
-} phpdbg_watch_collision;
+ char flags;
+ struct _phpdbg_watch_element *child; /* always set for implicit watches */
+ struct _phpdbg_watch_element *parent;
+ HashTable child_container; /* children of this watch element for recursive array elements */
+ HashTable *parent_container; /* container of the value */
+ zend_string *name_in_parent;
+ zend_string *str;
+ union {
+ zval zv;
+ zend_refcounted ref;
+ HashTable ht;
+ } backup; /* backup for when watchpoint gets dissociated */
+} phpdbg_watch_element;
typedef struct {
- dtor_func_t dtor;
- HashTable watches;
+ /* to watch rehashes (yes, this is not *perfect*, but good enough for everything in PHP...) */
+ phpdbg_watchpoint_t hash_watch; /* must be first element */
+ Bucket *last;
+ zend_string *last_str;
+ zend_ulong last_idx;
+
+ HashTable *ht;
+ size_t data_size;
+ HashTable watches; /* contains phpdbg_watch_element */
} phpdbg_watch_ht_info;
void phpdbg_setup_watchpoints(void);
+void phpdbg_destroy_watchpoints(void);
#ifndef _WIN32
int phpdbg_watchpoint_segfault_handler(siginfo_t *info, void *context);
diff --git a/sapi/phpdbg/tests/breakpoints_001.phpt b/sapi/phpdbg/tests/breakpoints_001.phpt
index 934f0d3554..17e7c65cbf 100644
--- a/sapi/phpdbg/tests/breakpoints_001.phpt
+++ b/sapi/phpdbg/tests/breakpoints_001.phpt
@@ -11,23 +11,23 @@ q
[Successful compilation of %s]
prompt> [Breakpoint #0 added at %s:3]
prompt> [Breakpoint #0 at %s:3, hits: 1]
->00003: echo 1;
- 00004: echo 2;
- 00005: echo 3;
+>00003: echo $i++;
+ 00004: echo $i++;
+ 00005: echo $i++;
prompt> [Breakpoint #1 added at %s:4]
prompt> 1
[Breakpoint #1 at %s:4, hits: 1]
->00004: echo 2;
- 00005: echo 3;
- 00006: echo 4;
+>00004: echo $i++;
+ 00005: echo $i++;
+ 00006: echo $i++;
prompt> 234
[Script ended normally]
prompt>
--FILE--
<?php
-
-echo 1;
-echo 2;
-echo 3;
-echo 4;
+$i = 1;
+echo $i++;
+echo $i++;
+echo $i++;
+echo $i++;
diff --git a/sapi/phpdbg/tests/breakpoints_002.phpt b/sapi/phpdbg/tests/breakpoints_002.phpt
index 18aaef1f36..96c98194b1 100644
--- a/sapi/phpdbg/tests/breakpoints_002.phpt
+++ b/sapi/phpdbg/tests/breakpoints_002.phpt
@@ -14,27 +14,27 @@ q
prompt> [Breakpoint #0 added at %s:4]
prompt> 1
[Breakpoint #0 at %s:4, hits: 1]
->00004: echo 2;
- 00005: echo 3;
- 00006: echo 4;
+>00004: echo $i++;
+ 00005: echo $i++;
+ 00006: echo $i++;
prompt> [Breakpoint #1 added at %s:3]
prompt> Do you really want to restart execution? (type y or n): [Breakpoint #1 at %s:3, hits: 1]
->00003: echo 1;
- 00004: echo 2;
- 00005: echo 3;
+>00003: echo $i++;
+ 00004: echo $i++;
+ 00005: echo $i++;
prompt> 1
[Breakpoint #0 at %s:4, hits: 1]
->00004: echo 2;
- 00005: echo 3;
- 00006: echo 4;
+>00004: echo $i++;
+ 00005: echo $i++;
+ 00006: echo $i++;
prompt> 234
[Script ended normally]
prompt>
--FILE--
<?php
-
-echo 1;
-echo 2;
-echo 3;
-echo 4;
+$i = 1;
+echo $i++;
+echo $i++;
+echo $i++;
+echo $i++;
diff --git a/sapi/phpdbg/tests/breakpoints_003.phpt b/sapi/phpdbg/tests/breakpoints_003.phpt
index 8caa64632b..12b2504487 100644
--- a/sapi/phpdbg/tests/breakpoints_003.phpt
+++ b/sapi/phpdbg/tests/breakpoints_003.phpt
@@ -16,8 +16,8 @@ prompt> [Deleted breakpoint #0]
prompt> [Breakpoint #1 added at %s:5]
prompt> 12
[Breakpoint #1 at %s:5, hits: 1]
->00005: echo 3;
- 00006: echo 4;
+>00005: echo $i++;
+ 00006: echo $i++;
00007:
prompt> [Deleted breakpoint #1]
prompt> Do you really want to restart execution? (type y or n): 1234
@@ -25,9 +25,9 @@ prompt> Do you really want to restart execution? (type y or n): 1234
prompt>
--FILE--
<?php
-
-echo 1;
-echo 2;
-echo 3;
-echo 4;
+$i = 1;
+echo $i++;
+echo $i++;
+echo $i++;
+echo $i++;
diff --git a/sapi/phpdbg/tests/breakpoints_004.phpt b/sapi/phpdbg/tests/breakpoints_004.phpt
index 27ebd0bea2..917e908efb 100644
--- a/sapi/phpdbg/tests/breakpoints_004.phpt
+++ b/sapi/phpdbg/tests/breakpoints_004.phpt
@@ -12,30 +12,30 @@ q
[Successful compilation of %s]
prompt> [Breakpoint #0 added at ZEND_ECHO]
prompt> [Breakpoint #0 in ZEND_ECHO at %s:3, hits: 1]
->00003: echo 1;
- 00004: echo 2;
- 00005: echo 3;
+>00003: echo $i++;
+ 00004: echo $i++;
+ 00005: echo $i++;
prompt> 1
[Breakpoint #0 in ZEND_ECHO at %s:4, hits: 2]
->00004: echo 2;
- 00005: echo 3;
- 00006: echo 4;
+>00004: echo $i++;
+ 00005: echo $i++;
+ 00006: echo $i++;
prompt> 2
[Breakpoint #0 in ZEND_ECHO at %s:5, hits: 3]
->00005: echo 3;
- 00006: echo 4;
+>00005: echo $i++;
+ 00006: echo $i++;
00007:
prompt> 3
[Breakpoint #0 in ZEND_ECHO at %s:6, hits: 4]
->00006: echo 4;
+>00006: echo $i++;
00007:
prompt> 4
[Script ended normally]
prompt>
--FILE--
<?php
-
-echo 1;
-echo 2;
-echo 3;
-echo 4;
+$i = 1;
+echo $i++;
+echo $i++;
+echo $i++;
+echo $i++;
diff --git a/sapi/phpdbg/tests/bug73794.phpt b/sapi/phpdbg/tests/bug73794.phpt
new file mode 100644
index 0000000000..8d5ba234c7
--- /dev/null
+++ b/sapi/phpdbg/tests/bug73794.phpt
@@ -0,0 +1,11 @@
+--TEST--
+Bug #73794 (Crash (out of memory) when using run and # command separator)
+--PHPDBG--
+r echo # quit
+--EXPECTF--
+[Successful compilation of %s]
+prompt> echo
+--FILE--
+<?php
+echo $argv[1];
+?>
diff --git a/sapi/phpdbg/tests/exceptions_001.phpt b/sapi/phpdbg/tests/exceptions_001.phpt
index 991ea1cc71..99989654cf 100644
--- a/sapi/phpdbg/tests/exceptions_001.phpt
+++ b/sapi/phpdbg/tests/exceptions_001.phpt
@@ -14,12 +14,12 @@ prompt> handle first
00017: } catch (\Exception $e) {
00018: var_dump($e);
prompt> frame #0: {closure}() at %s:16
-frame #1: {main} at %s:20
+frame #1: {main} at %s:22
prompt> 3
prompt> [Uncaught Error in %s on line 16]
Error: Call to undefined function foo() in %s:16
Stack trace:
-#0 %s(20): {closure}()
+#0 %s(22): {closure}()
#1 {main}
[Script ended normally]
prompt>
@@ -42,5 +42,7 @@ prompt>
foo(); // Error
} catch (\Exception $e) {
var_dump($e);
+ } catch (\ParseError $e) {
+ var_dump($e);
}
})();
diff --git a/sapi/phpdbg/tests/exceptions_003.phpt b/sapi/phpdbg/tests/exceptions_003.phpt
index 988e0c2d06..51090c081a 100644
--- a/sapi/phpdbg/tests/exceptions_003.phpt
+++ b/sapi/phpdbg/tests/exceptions_003.phpt
@@ -25,7 +25,7 @@ prompt> [L7 %s ECHO "ok\n"
00008: }
00009: } catch (Error $e) {
prompt> ok
-[L7 %s FAST_RET<TO_CATCH> ~%d J7 %s]
+[L7 %s FAST_RET ~%d try-catch(0) %s]
[L9 %s CATCH "Error" $e 1 %s]
>00005: x();
00006: } finally {
diff --git a/sapi/phpdbg/tests/finish_leave_001.phpt b/sapi/phpdbg/tests/finish_leave_001.phpt
index 774776c05f..e345752b00 100644
--- a/sapi/phpdbg/tests/finish_leave_001.phpt
+++ b/sapi/phpdbg/tests/finish_leave_001.phpt
@@ -1,5 +1,7 @@
--TEST--
test finish and leave commands
+--INI--
+opcache.optimization_level=0
--PHPDBG--
b bar
b 5
diff --git a/sapi/phpdbg/tests/stepping_001.phpt b/sapi/phpdbg/tests/stepping_001.phpt
index 9c3429897b..76577b2767 100644
--- a/sapi/phpdbg/tests/stepping_001.phpt
+++ b/sapi/phpdbg/tests/stepping_001.phpt
@@ -1,5 +1,7 @@
--TEST--
Stepping with exceptions must not be stuck at CATCH
+--INI--
+opcache.enable=0
--PHPDBG--
b ZEND_THROW
r
diff --git a/sapi/phpdbg/tests/watch_001.phpt b/sapi/phpdbg/tests/watch_001.phpt
index ea7da1a87d..e16681bf7c 100644
--- a/sapi/phpdbg/tests/watch_001.phpt
+++ b/sapi/phpdbg/tests/watch_001.phpt
@@ -1,5 +1,7 @@
--TEST--
Test simple recursive watchpoint
+--INI--
+opcache.optimization_level=0
--PHPDBG--
b 3
r
@@ -16,7 +18,7 @@ prompt> [Breakpoint #0 at %s:3, hits: 1]
>00003: $a = 1;
00004: $b = [$a];
00005:
-prompt> [Set recursive watchpoint on $b]
+prompt> [Added recursive watchpoint #0 for $b]
prompt> [Breaking on watchpoint $b]
Old value:
New value: Array ([0] => 1)
@@ -32,7 +34,7 @@ prompt> [Breaking on watchpoint $b]
Old value:
New value: 2
>00008:
-prompt> [$b was removed, removing watchpoint recursively]
+prompt> [$b has been removed, removing watchpoint recursively]
[Script ended normally]
prompt>
--FILE--
diff --git a/sapi/phpdbg/tests/watch_002.phpt b/sapi/phpdbg/tests/watch_002.phpt
new file mode 100644
index 0000000000..b57ca0a1d4
--- /dev/null
+++ b/sapi/phpdbg/tests/watch_002.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Test simple array watchpoint with replace
+--PHPDBG--
+b 6
+r
+w a $a
+c
+
+q
+--EXPECTF--
+[Successful compilation of %s]
+prompt> [Breakpoint #0 added at %s:6]
+prompt> [Breakpoint #0 at %s:6, hits: 1]
+>00006: $a[0] = 2;
+ 00007:
+ 00008: $a = [0 => 3, 1 => 4];
+prompt> [Added watchpoint #0 for $a[]]
+prompt> [Breaking on watchpoint $a[]]
+1 elements were added to the array
+>00009:
+prompt> [$a[] has been removed, removing watchpoint]
+[Script ended normally]
+prompt>
+--FILE--
+<?php
+
+$a = [];
+
+$a[0] = 1;
+$a[0] = 2;
+
+$a = [0 => 3, 1 => 4];
diff --git a/sapi/phpdbg/tests/watch_003.phpt b/sapi/phpdbg/tests/watch_003.phpt
new file mode 100644
index 0000000000..08868aedc3
--- /dev/null
+++ b/sapi/phpdbg/tests/watch_003.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Test simple watchpoint with replace
+--PHPDBG--
+b 6
+r
+w $a[0]
+c
+
+
+q
+--EXPECTF--
+[Successful compilation of %s]
+prompt> [Breakpoint #0 added at %s:6]
+prompt> [Breakpoint #0 at %s:6, hits: 1]
+>00006: $a[0] = 2;
+ 00007:
+ 00008: $a = [0 => 3, 1 => 4];
+prompt> [Added watchpoint #0 for $a[0]]
+prompt> [Breaking on watchpoint $a[0]]
+Old value: 1
+New value: 2
+>00008: $a = [0 => 3, 1 => 4];
+ 00009:
+prompt> [Breaking on watchpoint $a[0]]
+Old value: 2
+New value: 3
+>00009:
+prompt> [$a[0] has been removed, removing watchpoint]
+[Script ended normally]
+prompt>
+--FILE--
+<?php
+
+$a = [];
+
+$a[0] = 1;
+$a[0] = 2;
+
+$a = [0 => 3, 1 => 4];
diff --git a/sapi/phpdbg/tests/watch_004.phpt b/sapi/phpdbg/tests/watch_004.phpt
new file mode 100644
index 0000000000..878542937b
--- /dev/null
+++ b/sapi/phpdbg/tests/watch_004.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Test detection of inline string manipulations on zval watch
+--INI--
+opcache.optimization_level=0
+--PHPDBG--
+b 3
+r
+w $a
+c
+
+
+q
+--EXPECTF--
+[Successful compilation of %s]
+prompt> [Breakpoint #0 added at %s:3]
+prompt> [Breakpoint #0 at %s:3, hits: 1]
+>00003: $b = "a";
+ 00004: $a = $b.$b;
+ 00005: $a[1] = "b";
+prompt> [Added watchpoint #0 for $a]
+prompt> [Breaking on watchpoint $a]
+Old value:
+New value: aa
+>00005: $a[1] = "b";
+ 00006:
+prompt> [Breaking on watchpoint $a]
+Old value: aa
+New value: ab
+>00006:
+prompt> [$a has been removed, removing watchpoint]
+[Script ended normally]
+prompt>
+--FILE--
+<?php
+
+$b = "a";
+$a = $b.$b;
+$a[1] = "b";
diff --git a/sapi/phpdbg/tests/watch_005.phpt b/sapi/phpdbg/tests/watch_005.phpt
new file mode 100644
index 0000000000..d6bae9d3f3
--- /dev/null
+++ b/sapi/phpdbg/tests/watch_005.phpt
@@ -0,0 +1,48 @@
+--TEST--
+Test proper watch comparisons when having multiple levels of indirection from a zval to its value
+--PHPDBG--
+b 3
+r
+w r $a
+c
+
+
+
+q
+--EXPECTF--
+[Successful compilation of %s]
+prompt> [Breakpoint #0 added at %s:3]
+prompt> [Breakpoint #0 at %s:3, hits: 1]
+>00003: $b = "a";
+ 00004: $a = $b.$b;
+ 00005: $c = &$a;
+prompt> [Added recursive watchpoint #0 for $a]
+prompt> [Breaking on watchpoint $a]
+Old value:
+New value: aa
+>00005: $c = &$a;
+ 00006: $a[1] = "b";
+ 00007:
+prompt> [Breaking on watchpoint $a]
+Old value inaccessible or destroyed
+New value (reference): aa
+>00006: $a[1] = "b";
+ 00007:
+ 00008: exit;
+prompt> [Breaking on watchpoint $a]
+Old value: aa
+New value: ab
+>00008: exit;
+ 00009:
+prompt> [$a has been removed, removing watchpoint recursively]
+[Script ended normally]
+prompt>
+--FILE--
+<?php
+
+$b = "a";
+$a = $b.$b;
+$c = &$a;
+$a[1] = "b";
+
+exit;
diff --git a/sapi/phpdbg/tests/watch_006.phpt b/sapi/phpdbg/tests/watch_006.phpt
new file mode 100644
index 0000000000..bf38b8eff1
--- /dev/null
+++ b/sapi/phpdbg/tests/watch_006.phpt
@@ -0,0 +1,71 @@
+--TEST--
+Test multiple watch elements pointing to the same watchpoint
+--PHPDBG--
+b 4
+r
+w $a[0]
+w r $b
+c
+
+
+
+
+
+
+q
+--EXPECTF--
+[Successful compilation of %s]
+prompt> [Breakpoint #0 added at %s:4]
+prompt> [Breakpoint #0 at %s:4, hits: 1]
+>00004: $a[0] = 1;
+ 00005: $b = &$a;
+ 00006: $a[0] = 2;
+prompt> [Added watchpoint #0 for $a[0]]
+prompt> [Added recursive watchpoint #1 for $b]
+prompt> [Breaking on watchpoint $a[0]]
+Old value: 0
+New value: 1
+>00005: $b = &$a;
+ 00006: $a[0] = 2;
+ 00007: $a[1] = 3;
+prompt> [Breaking on watchpoint $b]
+Old value:
+New value (reference): Array ([0] => 1)
+>00006: $a[0] = 2;
+ 00007: $a[1] = 3;
+ 00008: $c = [1];
+prompt> [Breaking on watchpoint $a[0]]
+Old value: 1
+New value: 2
+>00007: $a[1] = 3;
+ 00008: $c = [1];
+ 00009: $b = &$c;
+prompt> [Element 1 has been added to watchpoint]
+[Breaking on watchpoint $b[]]
+1 elements were added to the array
+>00008: $c = [1];
+ 00009: $b = &$c;
+ 00010:
+prompt> [Breaking on watchpoint $b]
+Old value inaccessible or destroyed
+New value (reference): Array ([0] => 2,[1] => 3)
+>00009: $b = &$c;
+ 00010:
+prompt> [Breaking on watchpoint $b]
+Old value inaccessible or destroyed
+New value (reference): Array ([0] => 1)
+>00010:
+prompt> [$b has been removed, removing watchpoint recursively]
+[$a[0] has been removed, removing watchpoint]
+[Script ended normally]
+prompt>
+--FILE--
+<?php
+
+$a = [0];
+$a[0] = 1;
+$b = &$a;
+$a[0] = 2;
+$a[1] = 3;
+$c = [1];
+$b = &$c;