]> granicus.if.org Git - php/commitdiff
support conditional breakpoints
authorkrakjoe <joe.watkins@live.co.uk>
Wed, 13 Nov 2013 14:22:01 +0000 (14:22 +0000)
committerkrakjoe <joe.watkins@live.co.uk>
Wed, 13 Nov 2013 14:22:01 +0000 (14:22 +0000)
config.m4
phpdbg.c
phpdbg.h
phpdbg_bp.c
phpdbg_bp.h
phpdbg_prompt.c
test.php

index 92f626e86557d0639e2c4a863953d2f89ec13646..7d6b8262bfed669306a1aac9b5f166ed54481c4d 100644 (file)
--- a/config.m4
+++ b/config.m4
@@ -9,7 +9,7 @@ if test "$PHP_PHPDBG" != "no"; then
   AC_DEFINE(HAVE_PHPDBG, 1, [ ])
 
   PHP_PHPDBG_CFLAGS="-I$abc_srcdir"
-  PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c"
+  PHP_PHPDBG_FILES="phpdbg.c phpdbg_prompt.c phpdbg_help.c phpdbg_break.c phpdbg_print.c phpdbg_bp.c phpdbg_opcode.c phpdbg_list.c phpdbg_utils.c"
 
   PHP_SUBST(PHP_PHPDBG_CFLAGS)
   PHP_SUBST(PHP_PHPDBG_FILES)
index 24c5c1078a182510b1135513b70ad987a79ab6a1..f8bdd6aa784f335c24ec45fce3f3d3d2ceaee582 100644 (file)
--- a/phpdbg.c
+++ b/phpdbg.c
@@ -64,13 +64,30 @@ static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */
     zend_hash_destroy((HashTable*)brake);
 } /* }}} */
 
+static void php_phpdbg_destroy_bp_condition(void *data) /* {{{ */
+{
+    phpdbg_breakcond_t *brake = *(phpdbg_breakcond_t**) data;
+    
+    if (brake) {
+        if (brake->ops) {
+            TSRMLS_FETCH();
+            
+            destroy_op_array(
+                brake->ops TSRMLS_CC);
+            efree(brake->ops);
+        }
+        zval_dtor(&brake->code);
+    }
+} /* }}} */
+
 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_SYM], 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
     zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 0);
     zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD], 8, NULL, php_phpdbg_destroy_bp_methods, 0);
-
+    zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], 8, NULL, php_phpdbg_destroy_bp_condition, 0);
+    
        return SUCCESS;
 } /* }}} */
 
@@ -80,7 +97,8 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
     zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
     zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
     zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
-
+    zend_hash_destroy(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+    
     if (PHPDBG_G(exec)) {
         efree(PHPDBG_G(exec));
     }
@@ -455,7 +473,7 @@ int main(int argc, char **argv) /* {{{ */
                    zend_try {
                        phpdbg_interactive(TSRMLS_C);
                    } zend_catch {
-
+                
                    } zend_end_try();
                } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
 
index 063cce1f9a9cdb3983a9116ca73e3fcf492fed59..7ebc35aa5e33defdcb3fc5ac5508826b6ecba28c 100644 (file)
--- a/phpdbg.h
+++ b/phpdbg.h
 #define PHPDBG_BREAK_SYM        1
 #define PHPDBG_BREAK_OPLINE     2
 #define PHPDBG_BREAK_METHOD     3
-#define PHPDBG_BREAK_TABLES     4 /* }}} */
+#define PHPDBG_BREAK_COND       4
+#define PHPDBG_BREAK_TABLES     5 /* }}} */
 
 /* {{{ flags */
-#define PHPDBG_HAS_FILE_BP      0x00000001
-#define PHPDBG_HAS_SYM_BP       0x00000010
-#define PHPDBG_HAS_OPLINE_BP    0x00000100
-#define PHPDBG_HAS_METHOD_BP    0x00001000
-#define PHPDBG_BP_MASK          (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP)
+#define PHPDBG_HAS_FILE_BP      0x000000000001
+#define PHPDBG_HAS_SYM_BP       0x000000000010
+#define PHPDBG_HAS_OPLINE_BP    0x000000000100
+#define PHPDBG_HAS_METHOD_BP    0x000000001000
+#define PHPDBG_HAS_COND_BP      0x000000010000
+#define PHPDBG_IN_COND_BP       0x000000100000
+#define PHPDBG_BP_MASK          (PHPDBG_HAS_FILE_BP|PHPDBG_HAS_SYM_BP|PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_OPLINE_BP|PHPDBG_HAS_COND_BP)
 
-#define PHPDBG_IS_STEPPING      0x00010000
-#define PHPDBG_IS_QUIET         0x00100000
-#define PHPDBG_IS_QUITTING      0x01000000
-#define PHPDBG_IS_COLOURED      0x10000000
+#define PHPDBG_IS_STEPPING      0x000001000000
+#define PHPDBG_IS_QUIET         0x000010000000
+#define PHPDBG_IS_QUITTING      0x000100000000
+#define PHPDBG_IS_COLOURED      0x001000000000
 
 #define PHPDBG_DEFAULT_FLAGS    (PHPDBG_IS_QUIET|PHPDBG_IS_COLOURED) /* }}} */
 
index 988898f22edb831fcd823577e31d33342b3a2821..fdf9df78e5c120f0b23e1804508858b6883cb6a5 100644 (file)
@@ -23,6 +23,7 @@
 #include "phpdbg.h"
 #include "phpdbg_bp.h"
 #include "phpdbg_utils.h"
+#include "zend_globals.h"
 
 ZEND_EXTERN_MODULE_GLOBALS(phpdbg);
 
@@ -167,6 +168,59 @@ void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t opline TSRMLS_DC) /* {{
        }
 } /* }}} */
 
+void phpdbg_set_breakpoint_expression(const char* expr, size_t expr_len TSRMLS_DC) /* {{{ */
+{
+    zend_ulong hash = zend_inline_hash_func(expr, expr_len);
+    
+    if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash)) {
+        phpdbg_breakcond_t new_break;
+        
+        zend_op_array *ops = NULL;
+        zend_uint cops = CG(compiler_options);
+
+        ZVAL_STRINGL(&new_break.code, expr, expr_len, 1);
+        
+           new_break.id = PHPDBG_G(bp_count)++;
+        
+        cops = CG(compiler_options);
+        
+        CG(compiler_options) = ZEND_COMPILE_DEFAULT_FOR_EVAL;
+        {   
+            zval pv;
+            
+            Z_STRLEN(pv) = expr_len + sizeof("return ;") - 1;
+               Z_STRVAL(pv) = emalloc(Z_STRLEN(pv) + 1);
+               memcpy(Z_STRVAL(pv), "return ", sizeof("return ") - 1);
+               memcpy(Z_STRVAL(pv) + sizeof("return ") - 1, expr, expr_len);
+               Z_STRVAL(pv)[Z_STRLEN(pv) - 1] = ';';
+               Z_STRVAL(pv)[Z_STRLEN(pv)] = '\0';
+               Z_TYPE(pv) = IS_STRING;
+               
+               new_break.ops = zend_compile_string(
+                       &pv, "Conditional Breakpoint Code" TSRMLS_CC);
+
+                   if (new_break.ops) {
+                       phpdbg_breakcond_t *broken;
+                       
+                       zend_hash_index_update(
+                       &PHPDBG_G(bp)[PHPDBG_BREAK_COND], hash, &new_break, sizeof(phpdbg_breakcond_t), (void**)&broken);
+                   phpdbg_notice(
+                       "Conditional breakpoint #%d added %s/%p", broken->id, Z_STRVAL(broken->code), broken->ops);
+                       PHPDBG_G(flags) |= PHPDBG_HAS_COND_BP;
+                       
+                   } else {
+                        phpdbg_error(
+                           "Failed to compile code for expression %s", expr);
+                        zval_dtor(&new_break.code);
+                        PHPDBG_G(bp_count)--;
+                   }
+        }
+               CG(compiler_options) = cops;
+    } else {
+        phpdbg_notice("Conditional break %s exists", expr);
+    }
+} /* }}} */
+
 int phpdbg_find_breakpoint_file(zend_op_array *op_array TSRMLS_DC) /* {{{ */
 {
        size_t name_len = strlen(op_array->filename);
@@ -191,6 +245,8 @@ int phpdbg_find_breakpoint_file(zend_op_array *op_array TSRMLS_DC) /* {{{ */
        return FAILURE;
 } /* }}} */
 
+
+
 int phpdbg_find_breakpoint_symbol(zend_function *fbc TSRMLS_DC) /* {{{ */
 {
        const char *fname;
@@ -267,13 +323,84 @@ int phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t opline TSRMLS_DC) /* {{{ *
        return FAILURE;
 } /* }}} */
 
+int phpdbg_find_conditional_breakpoint(TSRMLS_D) /* {{{ */
+{
+       phpdbg_breakcond_t *bp;
+    HashPosition position;
+    int breakpoint = FAILURE;
+    
+    for (zend_hash_internal_pointer_reset_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position);
+         zend_hash_get_current_data_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], (void*)&bp, &position) == SUCCESS;
+         zend_hash_move_forward_ex(&PHPDBG_G(bp)[PHPDBG_BREAK_COND], &position)) {
+         
+         zval *retval = NULL;
+         int orig_interactive = CG(interactive);
+         zval **orig_retval = EG(return_value_ptr_ptr);
+         zend_op_array *orig_ops = EG(active_op_array);
+         zend_op **orig_opline = EG(opline_ptr);
+         
+         ALLOC_INIT_ZVAL(retval);
+         
+         EG(return_value_ptr_ptr) = &retval;
+         EG(active_op_array) = bp->ops;
+         EG(no_extensions) = 1;
+         
+         if (!EG(active_symbol_table)) {
+            zend_rebuild_symbol_table(TSRMLS_C);
+         }
+         
+         CG(interactive) = 0;
+         
+         zend_try {
+            PHPDBG_G(flags) |= PHPDBG_IN_COND_BP;
+            zend_execute(   
+                EG(active_op_array) TSRMLS_CC);
+            if (i_zend_is_true(retval)) {
+                breakpoint = SUCCESS;
+            }
+         } zend_catch {
+            phpdbg_error(
+                "Error detected while evaluating expression %s", Z_STRVAL(bp->code));
+            CG(interactive) = orig_interactive;
+             
+            EG(no_extensions)=1;
+            EG(return_value_ptr_ptr) = orig_retval;
+            EG(active_op_array) = orig_ops;
+            EG(opline_ptr) = orig_opline;
+            PHPDBG_G(flags) &= ~PHPDBG_IN_COND_BP;
+         } zend_end_try();
+         
+         CG(interactive) = orig_interactive;
+         
+         EG(no_extensions)=1;
+         EG(return_value_ptr_ptr) = orig_retval;
+         EG(active_op_array) = orig_ops;
+         EG(opline_ptr) = orig_opline;
+         PHPDBG_G(flags) &= ~PHPDBG_IN_COND_BP;
+         
+         if (breakpoint == SUCCESS) {
+            break;
+         }
+    }
+    
+    if (breakpoint == SUCCESS) {
+        phpdbg_notice("Conditional breakpoint #%d: (%s) %s:%u",
+                   bp->id, Z_STRVAL(bp->code),
+                       zend_get_executed_filename(TSRMLS_C),
+                       zend_get_executed_lineno(TSRMLS_C));
+    }
+    
+       return breakpoint;
+} /* }}} */
+
 void phpdbg_clear_breakpoints(TSRMLS_D) /* {{{ */
 {
     zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_FILE]);
     zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_SYM]);
     zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE]);
     zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD]);
-
+    zend_hash_clean(&PHPDBG_G(bp)[PHPDBG_BREAK_COND]);
+    
     PHPDBG_G(flags) &= ~PHPDBG_BP_MASK;
 
     PHPDBG_G(bp_count) = 0;
index d6167597f49bf8df8a93ad6b0a2023cb2861cf0f..42c02c3a2bc434ccdc37355d495cc9ae1690e927 100644 (file)
@@ -60,16 +60,27 @@ typedef struct _phpdbg_breakline_t {
     int id;
 } phpdbg_breakline_t;
 
+/**
+ * Breakpoint opline based representation
+ */
+typedef struct _phpdbg_breakcond_t {
+    zval            code;
+    zend_op_array   *ops;
+    int id;
+} phpdbg_breakcond_t;
+
 void phpdbg_set_breakpoint_file(const char*, long TSRMLS_DC);
 void phpdbg_set_breakpoint_symbol(const char* TSRMLS_DC);
 void phpdbg_set_breakpoint_method(const char*, const char* TSRMLS_DC);
 void phpdbg_set_breakpoint_opline(zend_ulong TSRMLS_DC);
 void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t TSRMLS_DC);
+void phpdbg_set_breakpoint_expression(const char*, size_t TSRMLS_DC);
 
 int phpdbg_find_breakpoint_file(zend_op_array* TSRMLS_DC);
 int phpdbg_find_breakpoint_symbol(zend_function* TSRMLS_DC);
 int phpdbg_find_breakpoint_method(zend_op_array* TSRMLS_DC);
 int phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t TSRMLS_DC);
+int phpdbg_find_conditional_breakpoint(TSRMLS_D);
 
 void phpdbg_clear_breakpoints(TSRMLS_D);
 void phpdbg_print_breakpoints(zend_ulong type TSRMLS_DC);
index 85c5345fcc8ba2897adc8611614ff669c90aef3b..e9f220aaa9017c9f078a57d2dca8dedded5cdc95 100644 (file)
@@ -24,6 +24,7 @@
 #include "phpdbg.h"
 #include "phpdbg_help.h"
 #include "phpdbg_print.h"
+#include "phpdbg_break.h"
 #include "phpdbg_bp.h"
 #include "phpdbg_opcode.h"
 #include "phpdbg_list.h"
@@ -122,6 +123,10 @@ static PHPDBG_COMMAND(run) /* {{{ */
     }
 
        if (PHPDBG_G(ops) || PHPDBG_G(exec)) {
+               zend_op **orig_opline = EG(opline_ptr);
+               zend_op_array *orig_op_array = EG(active_op_array);
+               zval **orig_retval_ptr = EG(return_value_ptr_ptr);
+               
                if (!PHPDBG_G(ops)) {
                        if (phpdbg_compile(TSRMLS_C) == FAILURE) {
                                phpdbg_error("Failed to compile %s, cannot run", PHPDBG_G(exec));
@@ -131,16 +136,28 @@ static PHPDBG_COMMAND(run) /* {{{ */
 
                EG(active_op_array) = PHPDBG_G(ops);
                EG(return_value_ptr_ptr) = &PHPDBG_G(retval);
-
+        if (!EG(active_symbol_table)) {
+            zend_rebuild_symbol_table(TSRMLS_C);
+        }
+        
                zend_try {
-                       zend_execute(EG(active_op_array) TSRMLS_CC);
+                       zend_execute(
+                           EG(active_op_array) TSRMLS_CC);
                } zend_catch {
+                   EG(active_op_array) = orig_op_array;
+                   EG(opline_ptr) = orig_opline;
+                   EG(return_value_ptr_ptr) = orig_retval_ptr;
+                   
                        if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
                                phpdbg_error("Caught excetion in VM");
                                return FAILURE;
                        } else return SUCCESS;
                } zend_end_try();
 
+        EG(active_op_array) = orig_op_array;
+           EG(opline_ptr) = orig_opline;
+           EG(return_value_ptr_ptr) = orig_retval_ptr;
+        
                return SUCCESS;
        } else {
                phpdbg_error("Nothing to execute!");
@@ -257,6 +274,13 @@ static PHPDBG_COMMAND(print) /* {{{ */
 static PHPDBG_COMMAND(break) /* {{{ */
 {
        char *line_pos;
+       
+       if (expr_len > 0L) {
+           /* allow advanced breakers to run */
+           if (phpdbg_do_cmd(phpdbg_break_commands, (char*)expr, expr_len TSRMLS_CC) == SUCCESS) {
+                       return SUCCESS;
+               }
+       }
 
     if (expr_len <= 0L) {
         phpdbg_error("No expression found");
@@ -668,52 +692,72 @@ zend_vm_enter:
         phpdbg_print_opline(
                    execute_data, 0 TSRMLS_CC);
 
-        if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_BP)
-            && phpdbg_find_breakpoint_file(execute_data->op_array TSRMLS_CC) == SUCCESS) {
-            while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) {
-                if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
-                    continue;
-                }
+        /* allow conditional breakpoints to access the vm uninterrupted */
+        if (!(PHPDBG_G(flags) & PHPDBG_IN_COND_BP)) {
+            
+            if ((PHPDBG_G(flags) & PHPDBG_HAS_COND_BP)
+                && phpdbg_find_conditional_breakpoint(TSRMLS_C) == SUCCESS) {
+                do {
+                    switch (phpdbg_interactive(TSRMLS_C)) {
+                        case PHPDBG_NEXT:
+                            goto next;
+                    }
+                } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
+            }
+
+            if ((PHPDBG_G(flags) & PHPDBG_HAS_FILE_BP)
+                && phpdbg_find_breakpoint_file(execute_data->op_array TSRMLS_CC) == SUCCESS) {
+                do {
+                    switch (phpdbg_interactive(TSRMLS_C)) {
+                        case PHPDBG_NEXT:
+                            goto next;
+                    }
+                } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
             }
-        }
 
-        if ((PHPDBG_G(flags) & (PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_SYM_BP))) {
-            zend_execute_data *previous = execute_data->prev_execute_data;
-            if (previous && previous != execute_data && previous->opline) {
-                /* check we are the beginning of a function entry */
-                if (execute_data->opline == EG(active_op_array)->opcodes) {
-                    switch (previous->opline->opcode) {
-                        case ZEND_DO_FCALL:
-                        case ZEND_DO_FCALL_BY_NAME:
-                        case ZEND_INIT_STATIC_METHOD_CALL: {
-                            if (phpdbg_find_breakpoint_symbol(previous->function_state.function TSRMLS_CC) == SUCCESS) {
-                                while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) {
-                                    if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
-                                        continue;
-                                    }
+            if ((PHPDBG_G(flags) & (PHPDBG_HAS_METHOD_BP|PHPDBG_HAS_SYM_BP))) {
+                zend_execute_data *previous = execute_data->prev_execute_data;
+                if (previous && previous != execute_data && previous->opline) {
+                    /* check we are the beginning of a function entry */
+                    if (execute_data->opline == EG(active_op_array)->opcodes) {
+                        switch (previous->opline->opcode) {
+                            case ZEND_DO_FCALL:
+                            case ZEND_DO_FCALL_BY_NAME:
+                            case ZEND_INIT_STATIC_METHOD_CALL: {
+                                if (phpdbg_find_breakpoint_symbol(previous->function_state.function TSRMLS_CC) == SUCCESS) {
+                                    do {
+                                        switch (phpdbg_interactive(TSRMLS_C)) {
+                                            case PHPDBG_NEXT:
+                                                goto next;
+                                        }
+                                    } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
                                 }
-                            }
-                        } break;
+                            } break;
+                        }
                     }
                 }
             }
-        }
 
-        if ((PHPDBG_G(flags) & PHPDBG_HAS_OPLINE_BP)
-            && phpdbg_find_breakpoint_opline(execute_data->opline TSRMLS_CC) == SUCCESS) {
-            while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) {
-                if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
-                    continue;
-                }
+            if ((PHPDBG_G(flags) & PHPDBG_HAS_OPLINE_BP)
+                && phpdbg_find_breakpoint_opline(execute_data->opline TSRMLS_CC) == SUCCESS) {
+                do {
+                    switch (phpdbg_interactive(TSRMLS_C)) {
+                        case PHPDBG_NEXT:
+                            goto next;
+                    }
+                } while(!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING));
             }
         }
 
+next:
         PHPDBG_G(vmret) = execute_data->opline->handler(execute_data TSRMLS_CC);
 
-        if ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)) {
-            while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) {
-                if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
-                    continue;
+        if (!(PHPDBG_G(flags) & PHPDBG_IN_COND_BP)) {
+            if ((PHPDBG_G(flags) & PHPDBG_IS_STEPPING)) {
+                while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) {
+                    if (!(PHPDBG_G(flags) & PHPDBG_IS_QUITTING)) {
+                        continue;
+                    }
                 }
             }
         }
index a9743f42137d8a630c0eb8e8f6c54e360949e190..ef786ca047c1559380ae2388dbd3f039c82b381b 100644 (file)
--- a/test.php
+++ b/test.php
@@ -9,6 +9,8 @@ class phpdbg {
 
 $dbg = new phpdbg();
 
+$test = 1;
+
 var_dump(
     $dbg->isGreat("PHP Rocks !!"));
 ?>