]> granicus.if.org Git - php/commitdiff
break on class methods
authorkrakjoe <joe.watkins@live.co.uk>
Tue, 12 Nov 2013 00:27:48 +0000 (00:27 +0000)
committerkrakjoe <joe.watkins@live.co.uk>
Tue, 12 Nov 2013 00:27:48 +0000 (00:27 +0000)
don't dtor eval'd retval
more reliable break on methods

phpdbg.c
phpdbg.h
phpdbg_bp.c
phpdbg_bp.h
phpdbg_help.c
phpdbg_prompt.c
test.php

index d24016275c7baed5ea0dd7c36156d7d63303ac02..a37781b245367c94a25c1661398a00bf6b84d99d 100644 (file)
--- a/phpdbg.c
+++ b/phpdbg.c
@@ -41,6 +41,7 @@ static inline void php_phpdbg_globals_ctor(zend_phpdbg_globals *pg) /* {{{ */
     pg->has_file_bp = 0;
     pg->has_sym_bp = 0;
     pg->has_opline_bp = 0;
+    pg->has_method_bp = 0;
 } /* }}} */
 
 static PHP_MINIT_FUNCTION(phpdbg) /* {{{ */
@@ -68,11 +69,17 @@ static void php_phpdbg_destroy_bp_opline(void *brake) /* {{{ */
        free((char*)((phpdbg_breakline_t*)brake)->name);
 } /* }}} */
 
+static void php_phpdbg_destroy_bp_methods(void *brake) /* {{{ */
+{
+    zend_hash_destroy((HashTable*)brake);
+} /* }}} */
+
 static PHP_RINIT_FUNCTION(phpdbg) /* {{{ */
 {
        zend_hash_init(&PHPDBG_G(bp_files),   8, NULL, php_phpdbg_destroy_bp_file, 0);
        zend_hash_init(&PHPDBG_G(bp_symbols), 8, NULL, php_phpdbg_destroy_bp_symbol, 0);
     zend_hash_init(&PHPDBG_G(bp_oplines), 8, NULL, php_phpdbg_destroy_bp_opline, 0);
+    zend_hash_init(&PHPDBG_G(bp_methods), 8, NULL, php_phpdbg_destroy_bp_methods, 0);
     
        return SUCCESS;
 } /* }}} */
@@ -82,6 +89,7 @@ static PHP_RSHUTDOWN_FUNCTION(phpdbg) /* {{{ */
     zend_hash_destroy(&PHPDBG_G(bp_files));
     zend_hash_destroy(&PHPDBG_G(bp_symbols));
     zend_hash_destroy(&PHPDBG_G(bp_oplines));
+    zend_hash_destroy(&PHPDBG_G(bp_methods));
     
     if (PHPDBG_G(exec)) {
         efree(PHPDBG_G(exec));
@@ -113,6 +121,7 @@ static PHP_FUNCTION(phpdbg_clear)
     zend_hash_clean(&PHPDBG_G(bp_files));
     zend_hash_clean(&PHPDBG_G(bp_symbols));
     zend_hash_clean(&PHPDBG_G(bp_oplines));
+    zend_hash_clean(&PHPDBG_G(bp_methods));
 } /* }}} */
 
 zend_function_entry phpdbg_user_functions[] = {
index 8adc69cf4f23fa271e0b0f7d35f3245b608c244d..7477c401116510a4e9590a0397a7bcefdda4e6e3 100644 (file)
--- a/phpdbg.h
+++ b/phpdbg.h
 typedef struct _phpdbg_command_t phpdbg_command_t;
 
 ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
-       HashTable bp_files;
-       HashTable bp_symbols;
-       HashTable bp_oplines;
+       HashTable bp_files;         /* file breakpoints */
+       HashTable bp_symbols;       /* symbol breakpoints */
+       HashTable bp_oplines;       /* opline breakpoints */
+       HashTable bp_methods;       /* method breakpoints */
        char *exec;                 /* file to execute */
        size_t exec_len;            /* size of exec */
        zend_op_array *ops;         /* op_array */
@@ -61,6 +62,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phpdbg)
        zend_bool has_file_bp;      /* file-based breakpoint has been set */
        zend_bool has_sym_bp;       /* symbol-based breakpoint has been set */
        zend_bool has_opline_bp;    /* opline-based breakpoint has been set */
+       zend_bool has_method_bp;    /* method-based breakpoint has been set */
        zend_bool quitting;         /* quitting flag */
        int quiet;                  /* quiet */
        phpdbg_command_t *last;     /* last command */
index 2949f971898e3b9aea8db60a5eaa619de7eaf4fa..c6ebef3f7321daf96be241e17d15636288f80d57 100644 (file)
@@ -32,6 +32,15 @@ static void phpdbg_llist_breakfile_dtor(void *data) /* {{{ */
        efree((char*)bp->filename);
 } /* }}} */
 
+
+static void phpdbg_class_breaks_dtor(void *data) /* {{{ */
+{
+    phpdbg_breakmethod_t *bp = (phpdbg_breakmethod_t*) data;
+    
+    efree((char*)bp->class_name);
+    efree((char*)bp->func_name);
+} /* }}} */
+
 void phpdbg_set_breakpoint_file(const char *path, long line_num TSRMLS_DC) /* {{{ */
 {
        phpdbg_breakfile_t new_break;
@@ -83,6 +92,42 @@ void phpdbg_set_breakpoint_symbol(const char *name TSRMLS_DC) /* {{{ */
        }
 } /* }}} */
 
+void phpdbg_set_breakpoint_method(const char* class_name, 
+                                  size_t class_len,
+                                  const char* func_name, 
+                                  size_t func_len TSRMLS_DC) /* {{{ */
+{
+    HashTable class_breaks, *class_table;
+    
+    if (zend_hash_find(&PHPDBG_G(bp_methods), class_name, class_len, (void**)&class_table) != SUCCESS) {
+        zend_hash_init(
+            &class_breaks, 8, NULL, phpdbg_class_breaks_dtor, 0);
+        zend_hash_update(
+            &PHPDBG_G(bp_methods), 
+            class_name, class_len, 
+            (void**)&class_breaks, sizeof(HashTable), (void**)&class_table);
+    }
+
+    if (!zend_hash_exists(class_table, func_name, func_len)) {
+        phpdbg_breakmethod_t new_break;
+        
+        PHPDBG_G(has_method_bp) = 1;
+        
+        new_break.class_name = class_name;
+        new_break.class_len = class_len;
+        new_break.func_name = func_name;
+        new_break.func_len = func_len;
+        new_break.id = PHPDBG_G(bp_count)++;
+                
+        zend_hash_update(class_table, func_name, func_len, &new_break, sizeof(phpdbg_breakmethod_t), NULL);
+        printf(
+            "[Breakpoint #%d added at %s::%s]\n", new_break.id, class_name, func_name);
+    } else {
+        printf(
+            "[Breakpoint exists at %s::%s]\n", class_name, func_name);
+    }
+} /* }}} */
+
 void phpdbg_set_breakpoint_opline(const char *name TSRMLS_DC) /* {{{ */
 {
        zend_ulong opline = strtoul(name, 0, 16);
@@ -172,6 +217,40 @@ int phpdbg_find_breakpoint_symbol(zend_function *fbc TSRMLS_DC) /* {{{ */
        return FAILURE;
 } /* }}} */
 
+int phpdbg_find_breakpoint_method(zend_function *fbc TSRMLS_DC) /* {{{ */
+{
+       HashTable *class_table;
+       phpdbg_breakmethod_t *bp;
+    zend_op_array *ops = NULL;
+    
+       if (fbc->type != ZEND_USER_FUNCTION) {
+               return FAILURE;
+       }
+
+    ops = ((zend_op_array*)fbc);
+    
+    if (!ops->scope) {
+        return FAILURE;
+    }
+    
+       if (zend_hash_find(&PHPDBG_G(bp_methods), ops->scope->name, ops->scope->name_length, 
+               (void**)&class_table) == SUCCESS) {
+               if (zend_hash_find(
+                       class_table,
+                       ops->function_name, 
+                       strlen(ops->function_name), (void**)&bp) == SUCCESS) {
+                       
+                   printf(
+                       "[Breakpoint #%d in %s::%s() at %s:%u]\n", bp->id, bp->class_name, bp->func_name,
+                           zend_get_executed_filename(TSRMLS_C),
+                           zend_get_executed_lineno(TSRMLS_C));
+                       return SUCCESS;
+               }
+       }
+
+       return FAILURE;
+} /* }}} */
+
 int phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t opline TSRMLS_DC) /* {{{ */
 {
        phpdbg_breakline_t *bp;
@@ -193,10 +272,12 @@ void phpdbg_clear_breakpoints(TSRMLS_D) /* {{{ */
     zend_hash_clean(&PHPDBG_G(bp_files));
     zend_hash_clean(&PHPDBG_G(bp_symbols));
     zend_hash_clean(&PHPDBG_G(bp_oplines));
-
+    zend_hash_clean(&PHPDBG_G(bp_methods));
+    
     PHPDBG_G(has_file_bp) = 0;
     PHPDBG_G(has_sym_bp) = 0;
     PHPDBG_G(has_opline_bp) = 0;
+    PHPDBG_G(has_method_bp) = 0;
     PHPDBG_G(bp_count) = 0;
 } /* }}} */
 
index 4f1a2e237f3fa9f93b4eebd76afbbb9567fe245c..77dfd4861e19af5b58be2d34b17cc63905b6dbb8 100644 (file)
@@ -40,6 +40,17 @@ typedef struct _phpdbg_breaksymbol_t {
        int id;
 } phpdbg_breaksymbol_t;
 
+/**
+ * Breakpoint method based representation
+ */
+typedef struct _phpdbg_breakmethod_t {
+    const char *class_name;
+    size_t      class_len;
+    const char *func_name;
+    size_t      func_len;
+    int id;
+} phpdbg_breakmethod_t;
+
 /**
  * Breakpoint opline based representation
  */
@@ -51,11 +62,13 @@ typedef struct _phpdbg_breakline_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*, size_t, const char*, size_t TSRMLS_DC);
 void phpdbg_set_breakpoint_opline(const char* TSRMLS_DC);
 void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_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_function* TSRMLS_DC);
 int phpdbg_find_breakpoint_opline(phpdbg_opline_ptr_t TSRMLS_DC);
 
 void phpdbg_clear_breakpoints(TSRMLS_D);
index ef8b6208b95e69a614984f9da40cb5c2aed430b4..00404a446b7cebc74abf2252a296f5f4f6132921 100644 (file)
@@ -85,12 +85,15 @@ PHPDBG_HELP(break) /* {{{ */
        printf("Setting a breakpoint stops execution at a specific stage, the syntax is:\n");
        printf("\tfile:line\n");
        printf("\tfunction\n");
+       printf("\t\\my\\class::method\n");
        printf("\t0x16\n");
        printf("For example:\n");
        printf("\tphpdbg> break test.php:1\n");
        printf("Will break execution on line 1 of test.php\n");
        printf("\tphpdbg> break my_function\n");
        printf("Will break execution on entry to my_function\n");
+       printf("\tphpdbg> break \\my\\class::method\n");
+       printf("Will break execution on entry to \\my\\class::method\n");
        printf("\tphpdbg> break 0x7ff68f570e08\n");
        printf("Will break at the opline with the address provided (addresses are shown during execution)\n");
        printf("It is important to note, an address is only valid for the current compiled representation of the script\n");
index 306e929c5a1c78fbb6afe2ed28ca9a34d43a0bbc..a29bc75c1c40ecab6fcb0fcc26d24d8a0c038659 100644 (file)
@@ -149,9 +149,9 @@ static PHPDBG_COMMAND(eval) /* {{{ */
        if (expr_len) {
                if (zend_eval_stringl((char*)expr, expr_len-1,
                        &retval, "eval()'d code" TSRMLS_CC) == SUCCESS) {
-                       zend_print_zval_r(&retval, 0 TSRMLS_CC);
+                       zend_print_zval_r(
+                           &retval, 0 TSRMLS_CC);
                        printf("\n");
-                       zval_dtor(&retval);
                }
        } else {
                printf("[No expression provided !]\n");
@@ -286,25 +286,57 @@ static PHPDBG_COMMAND(print) /* {{{ */
 
 static PHPDBG_COMMAND(break) /* {{{ */
 {
-       const char *line_pos = zend_memrchr(expr, ':', expr_len);
-
+       char *line_pos = NULL;
+    char *func_pos = NULL;
+    
+    if (!expr_len) {
+        printf(
+            "[No expression found]\n");
+        return FAILURE;
+    }
+    
+    line_pos  = strchr(expr, ':');
+    
        if (line_pos) {
-               char path[MAXPATHLEN], resolved_name[MAXPATHLEN];
-               long line_num = strtol(line_pos+1, NULL, 0);
-
-               if (line_num) {
-                   memcpy(path, expr, line_pos - expr);
-                   path[line_pos - expr] = 0;
-
-                   if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
-                           printf("[Failed to expand path %s]\n", path);
-                           return FAILURE;
+           if (!(func_pos=strchr(line_pos+1, ':'))) {
+               char path[MAXPATHLEN], resolved_name[MAXPATHLEN];
+                   long line_num = strtol(line_pos+1, NULL, 0);
+
+                   if (line_num) {
+                       memcpy(path, expr, line_pos - expr);
+                       path[line_pos - expr] = 0;
+
+                       if (expand_filepath(path, resolved_name TSRMLS_CC) == NULL) {
+                               printf("[Failed to expand path %s]\n", path);
+                               return FAILURE;
+                       }
+
+                       phpdbg_set_breakpoint_file(resolved_name, line_num TSRMLS_CC);
+                   } else {
+                       printf("[No line specified in expression %s]\n", expr);
+                       return FAILURE;
+                   }
+           } else {
+                   char *class;
+                   char *func;
+                   
+                   size_t func_len = strlen(func_pos+1),
+                          class_len = (line_pos - expr);
+                   
+                   if (func_len) {
+                       class = emalloc(class_len+1);
+                       func = emalloc(func_len+1);
+                       
+                       memcpy(class, expr, class_len);
+                       class[class_len]='\0';
+                       memcpy(func, func_pos+1, func_len);
+                       func[func_len]='\0';
+                       
+                       phpdbg_set_breakpoint_method(class, class_len, func, func_len TSRMLS_CC);
+                   } else {
+                       printf("[No function found in method expression %s]\n", expr);
+                       return FAILURE;
                    }
-
-                   phpdbg_set_breakpoint_file(resolved_name, line_num TSRMLS_CC);
-               } else {
-                   printf("[No line specified in expression %s]\n", expr);
-                   return FAILURE;
                }
        } else {
                if (expr_len > 2 && expr[0] == '0' && expr[1] == 'x') {
@@ -313,11 +345,16 @@ static PHPDBG_COMMAND(break) /* {{{ */
                    char name[200];
                    size_t name_len = strlen(expr);
 
-                   name_len = MIN(name_len, 200);
-                   memcpy(name, expr, name_len);
-                   name[name_len] = 0;
+                   if (name_len) {
+                       name_len = MIN(name_len, 200);
+                       memcpy(name, expr, name_len);
+                       name[name_len] = 0;
 
-                   phpdbg_set_breakpoint_symbol(name TSRMLS_CC);
+                       phpdbg_set_breakpoint_symbol(name TSRMLS_CC);
+                   } else {
+                       printf("[Malformed break command found]\n");
+                       return FAILURE;
+                   }
                }
        }
 
@@ -350,22 +387,28 @@ static int clean_non_persistent_function_full(zend_function *function TSRMLS_DC)
 
 static PHPDBG_COMMAND(clean) /* {{{ */
 {
-    printf("[Cleaning Environment:]\n");
-    printf("[\tClasses: %d]\n", zend_hash_num_elements(EG(class_table)));
-    printf("[\tFunctions: %d]\n", zend_hash_num_elements(EG(function_table)));
-    printf("[\tConstants: %d]\n", zend_hash_num_elements(EG(zend_constants)));
-    printf("[\tIncluded: %d]\n", zend_hash_num_elements(&EG(included_files)));
-
-    zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC);
-    zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC);
-    zend_hash_reverse_apply(EG(zend_constants), (apply_func_t) clean_non_persistent_constant_full TSRMLS_CC);
-    zend_hash_clean(&EG(included_files));
-
-    printf("[Clean Environment:]\n");
-    printf("[\tClasses: %d]\n", zend_hash_num_elements(EG(class_table)));
-    printf("[\tFunctions: %d]\n", zend_hash_num_elements(EG(function_table)));
-    printf("[\tConstants: %d]\n", zend_hash_num_elements(EG(zend_constants)));
-    printf("[\tIncluded: %d]\n", zend_hash_num_elements(&EG(included_files)));
+    if (!EG(in_execution)) {
+        printf("[Cleaning Environment:]\n");
+        printf("[\tClasses: %d]\n", zend_hash_num_elements(EG(class_table)));
+        printf("[\tFunctions: %d]\n", zend_hash_num_elements(EG(function_table)));
+        printf("[\tConstants: %d]\n", zend_hash_num_elements(EG(zend_constants)));
+        printf("[\tIncluded: %d]\n", zend_hash_num_elements(&EG(included_files)));
+
+        zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC);
+        zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC);
+        zend_hash_reverse_apply(EG(zend_constants), (apply_func_t) clean_non_persistent_constant_full TSRMLS_CC);
+        zend_hash_clean(&EG(included_files));
+
+        printf("[Clean Environment:]\n");
+        printf("[\tClasses: %d]\n", zend_hash_num_elements(EG(class_table)));
+        printf("[\tFunctions: %d]\n", zend_hash_num_elements(EG(function_table)));
+        printf("[\tConstants: %d]\n", zend_hash_num_elements(EG(zend_constants)));
+        printf("[\tIncluded: %d]\n", zend_hash_num_elements(&EG(included_files)));
+    } else {
+        printf(
+            "[Cannot clean environment while executing]\n");
+        return FAILURE;
+    }
 
     return SUCCESS;
 } /* }}} */
@@ -376,7 +419,8 @@ static PHPDBG_COMMAND(clear) /* {{{ */
     printf("[\tFile\t%d]\n", zend_hash_num_elements(&PHPDBG_G(bp_files)));
     printf("[\tSymbols\t%d]\n", zend_hash_num_elements(&PHPDBG_G(bp_symbols)));
     printf("[\tOplines\t%d]\n", zend_hash_num_elements(&PHPDBG_G(bp_oplines)));
-
+    printf("[\tMethods\t%d]\n", zend_hash_num_elements(&PHPDBG_G(bp_methods)));
+    
     phpdbg_clear_breakpoints(TSRMLS_C);
 
     return SUCCESS;
@@ -540,23 +584,29 @@ zend_vm_enter:
             }
         }
 
-        if (PHPDBG_G(has_sym_bp) && execute_data->opline->opcode != ZEND_RETURN) {
+        if ((PHPDBG_G(has_sym_bp)||PHPDBG_G(has_method_bp))) {
             zend_execute_data *previous = execute_data->prev_execute_data;
             if (previous && previous != execute_data && previous->opline) {
-                if (previous->opline->opcode == ZEND_DO_FCALL
-                    || previous->opline->opcode == ZEND_DO_FCALL_BY_NAME) {
-                    if (phpdbg_find_breakpoint_symbol(
-                        previous->function_state.function TSRMLS_CC) == SUCCESS) {
-                        while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) {
-                            if (!PHPDBG_G(quitting)) {
-                                continue;
+                /* 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 ||
+                                phpdbg_find_breakpoint_method(previous->function_state.function TSRMLS_CC) == SUCCESS) {
+                                while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) {
+                                    if (!PHPDBG_G(quitting)) {
+                                        continue;
+                                    }
+                                }
                             }
-                        }
+                        } break;
                     }
                 }
             }
         }
-
+        
         if (PHPDBG_G(has_opline_bp)
             && phpdbg_find_breakpoint_opline(execute_data->opline TSRMLS_CC) == SUCCESS) {
             while (phpdbg_interactive(TSRMLS_C) != PHPDBG_NEXT) {
index 20fc66c7c5beee27af5dbf09e5633dabe44cbf68..457e487db703d7eb48631c9bac07d73735c7cb91 100644 (file)
--- a/test.php
+++ b/test.php
@@ -1,22 +1,18 @@
 <?php
-phpdbg_clear();
 
-function test() {
-       echo "Hello World\n";
-       $hidden = "variable";
-       phpdbg_break();
+class my {
+    public function method() {
+        return $this;
+    }
 }
 
 function test2() {
     echo "Hello World 2\n";
 }
 
-if (!isset($greeting)) {
-    echo test();
-}
 
-phpdbg_break();
+$my = new my();
+var_dump($my->method());
 
-test2();
 return true;
 ?>