]> granicus.if.org Git - php/commitdiff
Break on uncaught exceptions in current frame in phpdbg
authorBob Weinand <bobwei9@hotmail.com>
Tue, 21 Apr 2015 21:09:09 +0000 (23:09 +0200)
committerBob Weinand <bobwei9@hotmail.com>
Tue, 21 Apr 2015 23:08:07 +0000 (01:08 +0200)
sapi/phpdbg/phpdbg.h
sapi/phpdbg/phpdbg_prompt.c
sapi/phpdbg/phpdbg_utils.c

index 6de6e9e2b8235f43ee1cac17d286320dda393075..2cb588d08643be01d36a44b9a921de2a396a4439 100644 (file)
@@ -233,6 +233,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
        HashTable registered;                        /* registered */
        HashTable seek;                              /* seek oplines */
        zend_execute_data *seek_ex;                  /* call frame of oplines to seek to */
+       zend_object *handled_exception;              /* last handled exception (prevent multiple handling of same exception) */
        phpdbg_frame_t frame;                        /* frame */
        uint32_t last_line;                          /* last executed line */
 
index ac3fb67dbdc73cc7092522c0d7bc37af47da531b..11af5bc79002a8fd6ba4dca9a5c0b2326c7ca228 100644 (file)
@@ -24,6 +24,7 @@
 #include "zend_compile.h"
 #include "zend_exceptions.h"
 #include "zend_vm.h"
+#include "zend_generators.h"
 #include "phpdbg.h"
 
 #include "phpdbg_help.h"
@@ -647,6 +648,7 @@ PHPDBG_COMMAND(run) /* {{{ */
                } else {
                        zend_rebuild_symbol_table();
                }
+               PHPDBG_G(handled_exception) = NULL;
 
                /* clean seek state */
                PHPDBG_G(flags) &= ~PHPDBG_SEEK_MASK;
@@ -1376,7 +1378,12 @@ void phpdbg_clean(zend_bool full) /* {{{ */
        }
 } /* }}} */
 
+/* code may behave weirdly if EG(exception) is set */
 #define DO_INTERACTIVE(allow_async_unsafe) do { \
+       if (exception) { \
+               ++GC_REFCOUNT(exception); \
+               zend_clear_exception(); \
+       } \
        if (!(PHPDBG_G(flags) & PHPDBG_IN_EVAL)) { \
                const char *file_char = zend_get_executed_filename(); \
                zend_string *file = zend_string_init(file_char, strlen(file_char), 0); \
@@ -1385,6 +1392,13 @@ void phpdbg_clean(zend_bool full) /* {{{ */
        } \
        \
        switch (phpdbg_interactive(allow_async_unsafe)) { \
+               zval zv; \
+               default: \
+                       if (exception) { \
+                               Z_OBJ(zv) = exception; \
+                               zend_throw_exception_internal(&zv); \
+                       } \
+                       /* fallthrough */ \
                case PHPDBG_LEAVE: \
                case PHPDBG_FINISH: \
                case PHPDBG_UNTIL: \
@@ -1408,6 +1422,8 @@ void phpdbg_execute_ex(zend_execute_data *execute_data) /* {{{ */
        PHPDBG_G(in_execution) = 1;
 
        while (1) {
+               zend_object *exception = EG(exception);
+
                if ((PHPDBG_G(flags) & PHPDBG_BP_RESOLVE_MASK)) {
                        /* resolve nth opline breakpoints */
                        phpdbg_resolve_op_array_breaks(&execute_data->func->op_array);
@@ -1419,6 +1435,28 @@ void phpdbg_execute_ex(zend_execute_data *execute_data) /* {{{ */
                }
 #endif
 
+               /* check for uncaught exceptions */
+               if (exception && PHPDBG_G(handled_exception) != exception) {
+                       zend_execute_data *prev_ex = execute_data;
+
+                       do {
+                               prev_ex = zend_generator_check_placeholder_frame(prev_ex);
+                               /* assuming that no internal functions will silently swallow exceptions ... */
+                               if (!prev_ex->func || !ZEND_USER_CODE(prev_ex->func->common.type)) {
+                                       continue;
+                               }
+
+                               if (phpdbg_check_caught_ex(prev_ex)) {
+                                       goto ex_is_caught;
+                               }
+                       } while ((prev_ex = prev_ex->prev_execute_data));
+
+                       PHPDBG_G(handled_exception) = EG(exception);
+                       phpdbg_error("exception", "name=\"%s\"", "Uncaught exception %s", exception->ce->name->val);
+                       DO_INTERACTIVE(1);
+               }
+ex_is_caught:
+
                /* allow conditional breakpoints and
                        initialization to access the vm uninterrupted */
                if ((PHPDBG_G(flags) & PHPDBG_IN_COND_BP) ||
@@ -1540,6 +1578,7 @@ next:
 
 /* only if *not* interactive and while executing */
 void phpdbg_force_interruption(void) /* {{{ */ {
+       zend_object *exception = EG(exception);
        zend_execute_data *data = EG(current_execute_data); /* should be always readable if not NULL */
 
        PHPDBG_G(flags) |= PHPDBG_IN_SIGNAL_HANDLER;
@@ -1581,4 +1620,3 @@ PHPDBG_COMMAND(eol) /* {{{ */
 
        return SUCCESS;
 } /* }}} */
-
index e1f6c59502b0a9073e6b6f065de83cdc8395df7a..fce042a2254a6fa9dd84d864be35155598e9f3cd 100644 (file)
@@ -726,8 +726,8 @@ PHPDBG_API zend_bool phpdbg_check_caught_ex(zend_execute_data *ex) {
 
        op_num = op - op_array->opcodes;
 
-       for (i = 0; i < op_array->last_try_catch && op_array->try_catch_array[i].try_op > op_num; i++) {
-               if (op_num < op_array->try_catch_array[i].catch_op || op_num < op_array->try_catch_array[i].finally_op) {
+       for (i = 0; i < op_array->last_try_catch && op_array->try_catch_array[i].try_op < op_num; i++) {
+               if (op_num <= op_array->try_catch_array[i].catch_op || op_num <= op_array->try_catch_array[i].finally_op) {
                        return 1;
                }
        }