From: Bob Weinand Date: Thu, 28 Nov 2013 20:36:45 +0000 (+0100) Subject: First semi-working version of oplines X-Git-Tag: php-5.6.0alpha1~110^2~38 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d7f6e884932423662aaf319bced391c91c76cb48;p=php First semi-working version of oplines --- diff --git a/phpdbg.c b/phpdbg.c index f15e023503..322d54431e 100644 --- a/phpdbg.c +++ b/phpdbg.c @@ -133,12 +133,15 @@ 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_FUNCTION_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); + zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], 8, NULL, php_phpdbg_destroy_bp_methods, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], 8, NULL, NULL, 0); zend_hash_init(&PHPDBG_G(bp)[PHPDBG_BREAK_OPCODE], 8, NULL, php_phpdbg_destroy_bp_opcode, 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); zend_hash_init(&PHPDBG_G(seek), 8, NULL, NULL, 0); zend_hash_init(&PHPDBG_G(registered), 8, NULL, php_phpdbg_destroy_registered, 0); + PHPDBG_G(opline_btree) = NULL; return SUCCESS; } /* }}} */ @@ -309,14 +312,14 @@ zend_function_entry phpdbg_user_functions[] = { static zend_module_entry sapi_phpdbg_module_entry = { STANDARD_MODULE_HEADER, - "phpdbg", + PHPDBG_NAME, phpdbg_user_functions, PHP_MINIT(phpdbg), NULL, PHP_RINIT(phpdbg), PHP_RSHUTDOWN(phpdbg), NULL, - "0.1", + PHPDBG_VERSION, STANDARD_MODULE_PROPERTIES }; @@ -458,6 +461,38 @@ static sapi_module_struct phpdbg_sapi_module = { }; /* }}} */ +void phpdbg_op_array_handler(zend_op_array *op_array) { + TSRMLS_FETCH(); + + phpdbg_save_oplines(op_array TSRMLS_CC); + phpdbg_resolve_op_array_breaks(op_array TSRMLS_CC); +} + +#ifndef ZEND_EXT_API +#define ZEND_EXT_API ZEND_DLEXPORT +#endif +ZEND_EXTENSION(); + +ZEND_DLEXPORT zend_extension zend_extension_entry = { + PHPDBG_NAME, + PHPDBG_VERSION, + PHPDBG_AUTHORS, + PHPDBG_URL, + "(c) 2013", + NULL, /* startup_func_t */ + NULL, /* shutdown_func_t */ + NULL, /* activate_func_t */ + NULL, /* deactivate_func_t */ + NULL, /* message_handler_func_t */ + phpdbg_op_array_handler, /* op_array_handler_func_t */ + NULL, /* statement_handler_func_t */ + NULL, /* fcall_begin_handler_func_t */ + NULL, /* fcall_end_handler_func_t */ + NULL, /* op_array_ctor_func_t */ + NULL, /* op_array_dtor_func_t */ + STANDARD_ZEND_EXTENSION_PROPERTIES +}; + const opt_struct OPTIONS[] = { /* {{{ */ {'c', 1, "ini path override"}, {'d', 1, "define ini entry on command line"}, @@ -717,6 +752,8 @@ phpdbg_main: phpdbg->ini_entries = ini_entries; if (phpdbg->startup(phpdbg) == SUCCESS) { + zend_register_extension(&zend_extension_entry, NULL); + zend_activate(TSRMLS_C); #ifdef ZEND_SIGNALS diff --git a/phpdbg.h b/phpdbg.h index b045d85fe0..628f38a0f3 100644 --- a/phpdbg.h +++ b/phpdbg.h @@ -80,13 +80,15 @@ #define PHPDBG_LEAVE 5 /* {{{ tables */ -#define PHPDBG_BREAK_FILE 0 -#define PHPDBG_BREAK_SYM 1 -#define PHPDBG_BREAK_OPLINE 2 -#define PHPDBG_BREAK_METHOD 3 -#define PHPDBG_BREAK_COND 4 -#define PHPDBG_BREAK_OPCODE 5 -#define PHPDBG_BREAK_TABLES 6 /* }}} */ +#define PHPDBG_BREAK_FILE 0 +#define PHPDBG_BREAK_SYM 1 +#define PHPDBG_BREAK_OPLINE 2 +#define PHPDBG_BREAK_METHOD 3 +#define PHPDBG_BREAK_COND 4 +#define PHPDBG_BREAK_OPCODE 5 +#define PHPDBG_BREAK_FUNCTION_OPLINE 6 +#define PHPDBG_BREAK_METHOD_OPLINE 7 +#define PHPDBG_BREAK_TABLES 8/* }}} */ /* {{{ flags */ #define PHPDBG_HAS_FILE_BP (1<<1) @@ -125,6 +127,9 @@ #endif /* }}} */ /* {{{ strings */ +#define PHPDBG_NAME "phpdbg" +#define PHPDBG_AUTHORS "Felipe Pena, Joe Watkins and Bob Weinand" /* Ordered by last name */ +#define PHPDBG_URL "http://phpdbg.com" #define PHPDBG_ISSUES "http://github.com/krakjoe/phpdbg/issues" #define PHPDBG_VERSION "0.2.0-dev" #define PHPDBG_INIT_FILENAME ".phpdbginit" @@ -137,8 +142,15 @@ #define PHPDBG_IO_FDS 3 /* }}} */ /* {{{ structs */ +typedef union _phpdbg_btree phpdbg_btree; +union _phpdbg_btree { + phpdbg_btree *branches[2]; + zend_op_array *op_array; +}; + ZEND_BEGIN_MODULE_GLOBALS(phpdbg) HashTable bp[PHPDBG_BREAK_TABLES]; /* break points */ + phpdbg_btree *opline_btree; /* opline root -> op_array */ HashTable registered; /* registered */ HashTable seek; /* seek oplines */ phpdbg_frame_t frame; /* frame */ diff --git a/phpdbg_bp.c b/phpdbg_bp.c index 75d0918401..22d376eb5f 100644 --- a/phpdbg_bp.c +++ b/phpdbg_bp.c @@ -44,6 +44,19 @@ static void phpdbg_class_breaks_dtor(void *data) /* {{{ */ efree((char*)bp->func_name); } /* }}} */ +static void phpdbg_opline_class_breaks_dtor(void *data) /* {{{ */ +{ + zend_hash_destroy((HashTable *)data); +} /* }}} */ + +static void phpdbg_opline_breaks_dtor(void *data) /* {{{ */ +{ + phpdbg_breakopline_t *bp = (phpdbg_breakopline_t *) data; + + efree((char*)bp->class_name); + efree((char*)bp->func_name); +} /* }}} */ + PHPDBG_API void phpdbg_export_breakpoints(FILE *handle TSRMLS_DC) /* {{{ */ { HashPosition position; @@ -251,10 +264,68 @@ PHPDBG_API void phpdbg_set_breakpoint_method(const char* class_name, const char* efree(lcname); } /* }}} */ +PHPDBG_API void phpdbg_save_oplines(zend_op_array *op_array TSRMLS_DC) { + phpdbg_btree **branch = &PHPDBG_G(opline_btree); + int i = sizeof(zend_ulong) * 8 - 1; + + do { + if (*branch == NULL) { + break; + } + branch = &(*branch)->branches[((zend_ulong)op_array->opcodes >> i) % 2]; + } while (i--); + + if (*branch == NULL) { + phpdbg_btree *memory = *branch = emalloc(i * sizeof(phpdbg_btree)); + while (i--) { + branch = &(*branch)->branches[((zend_ulong)op_array->opcodes >> i) % 2]; + *branch = ++memory; + } + } + + (*branch)->op_array = op_array; +} + PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ */ { if (!zend_hash_index_exists(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline)) { phpdbg_breakline_t new_break; + phpdbg_breakopline_t opline_break; + int i = sizeof(zend_ulong) * 8 - 1, last_superior_i = -1; + phpdbg_btree *branch = PHPDBG_G(opline_btree); + HashTable *insert = &PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]; + HashTable func_breaks, *func_table; + + do { + if ((opline >> i) % 2 == 0 && !branch->branches[0]) { + if (last_superior_i != -1) { + i = sizeof(zend_ulong) * 8 - 1; + do { + branch = branch->branches[(opline >> i) % 2 == 1 && branch->branches[1]]; + } while (i-- > last_superior_i); + do { + branch = branch->branches[!!branch->branches[1]]; + } while (i--); + } + break; + } + if ((opline >> i) % 2 == 1 && branch->branches[1]) { + if (branch->branches[0]) { + last_superior_i = i; + } + branch = branch->branches[1]; + } else { + branch = branch->branches[0]; + } + } while (--i); + + if (!branch || + (zend_ulong)(branch->op_array + branch->op_array->last) <= opline || + (opline - (zend_ulong)branch->op_array) % sizeof(zend_op) > 0) { + phpdbg_error("No opline could be found at 0x%lx", opline); + } + + opline_break.opline = (opline - (zend_ulong)branch->op_array) / sizeof(zend_op); PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP; @@ -265,6 +336,42 @@ PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline, &new_break, sizeof(phpdbg_breakline_t), NULL); + opline_break.func_len = branch->op_array->function_name?strlen(branch->op_array->function_name):0; + opline_break.func_name = opline_break.func_len?estrndup(branch->op_array->function_name, opline_break.func_len):""; + opline_break.id = new_break.id; + if (branch->op_array->scope) { + HashTable class_breaks, *class_table; + + if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], + branch->op_array->scope->name, + branch->op_array->scope->name_length, + (void **)&class_table + ) == FAILURE) { + zend_hash_init(&class_breaks, 8, NULL, phpdbg_opline_class_breaks_dtor, 0); + zend_hash_update( + &PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], + branch->op_array->scope->name, + branch->op_array->scope->name_length, + (void **)&class_breaks, sizeof(HashTable), (void **)&class_table); + } + + opline_break.class_len = branch->op_array->scope->name_length; + opline_break.class_name = estrndup(branch->op_array->scope->name, opline_break.class_len); + + insert = class_table; + } + + if (zend_hash_find(insert, opline_break.func_name, opline_break.func_len, (void **)&func_table) == FAILURE) { + zend_hash_init(&func_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0); + zend_hash_update( + insert, + opline_break.func_name, + opline_break.func_len, + (void **)&func_breaks, sizeof(HashTable), (void **)&func_table); + } + + zend_hash_index_update(func_table, opline_break.opline, &opline_break, sizeof(phpdbg_breakopline_t), NULL); + phpdbg_notice("Breakpoint #%d added at %#lx", new_break.id, new_break.opline); } else { @@ -272,6 +379,164 @@ PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong opline TSRMLS_DC) /* {{{ } } /* }}} */ +PHPDBG_API void phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array TSRMLS_DC) { + phpdbg_breakline_t opline_break; + if (op_array->last < brake->opline) { + if (brake->class_name == NULL) { + phpdbg_error("There are only %d oplines in function %s (breaking at opline %d impossible)", op_array->last, brake->func_name, brake->opline); + } else { + phpdbg_error("There are only %d oplines in method %s::%s (breaking at opline %d impossible)", op_array->last, brake->class_name, brake->func_name, brake->opline); + } + } + + opline_break.id = brake->id; + opline_break.opline = (zend_ulong)(op_array->opcodes + brake->opline); + opline_break.name = NULL; + + PHPDBG_G(flags) |= PHPDBG_HAS_OPLINE_BP; + + zend_hash_index_update(&PHPDBG_G(bp)[PHPDBG_BREAK_OPLINE], opline_break.opline, &opline_break, sizeof(phpdbg_breakline_t), NULL); +} + +PHPDBG_API void phpdbg_resolve_op_array_breaks(zend_op_array *op_array TSRMLS_DC) { + HashTable *func_table = &PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE]; + HashTable *oplines_table; + HashPosition position; + phpdbg_breakopline_t *brake; + + if (op_array->scope != NULL && + zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], op_array->scope->name, op_array->scope->name_length, (void **)&func_table) == FAILURE) { + return; + } + + if (zend_hash_find(func_table, op_array->function_name?op_array->function_name:"", op_array->function_name?strlen(op_array->function_name):0, (void **)&oplines_table) == FAILURE) { + return; + } + + for (zend_hash_internal_pointer_reset_ex(oplines_table, &position); + zend_hash_get_current_data_ex(oplines_table, (void**) &brake, &position) == SUCCESS; + zend_hash_move_forward_ex(oplines_table, &position)) { + zend_try { + phpdbg_resolve_op_array_break(brake, op_array TSRMLS_CC); + } zend_end_try(); + } +} + +PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break TSRMLS_DC) { + HashTable *func_table = EG(function_table); + zend_function *func; + + if (new_break->class_name != NULL) { + zend_class_entry **ce; + if (zend_hash_find(EG(class_table), new_break->class_name, new_break->class_len + 1, (void **)&ce) == FAILURE) { + return FAILURE; + } + func_table = &(*ce)->function_table; + } + + if (zend_hash_find(func_table, new_break->func_name, new_break->func_len + 1, (void **)&func) == FAILURE) { + if (new_break->class_name != NULL) { + phpdbg_error("Method %s doesn't exist in class %s", new_break->func_name, new_break->class_name); + } + return FAILURE; + } + + if (func->type != ZEND_USER_FUNCTION) { + if (new_break->class_name == NULL) { + phpdbg_error("%s is not an user defined function, no oplines exist", new_break->func_name); + } else { + phpdbg_error("%s::%s is not an user defined method, no oplines exist", new_break->class_name, new_break->func_name); + } + } + + phpdbg_resolve_op_array_break(new_break, &func->op_array TSRMLS_CC); + + return SUCCESS; +} + +PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const char *method, int opline TSRMLS_DC) /* {{{ */ +{ + phpdbg_breakopline_t new_break; + HashTable class_breaks, *class_table; + HashTable method_breaks, *method_table; + + new_break.func_len = strlen(method); + new_break.func_name = estrndup(method, new_break.func_len); + new_break.class_len = strlen(class); + new_break.class_name = estrndup(class, new_break.class_len); + new_break.opline = opline; + new_break.id = PHPDBG_G(bp_count)++; + + if (phpdbg_resolve_opline_break(&new_break TSRMLS_CC) == FAILURE) { + phpdbg_notice("Pending breakpoint #%d at %s::%s:%d", new_break.id, new_break.class_name, new_break.func_name, opline); + } else { + phpdbg_notice("Breakpoint #%d added at %s::%s:%d", new_break.id, new_break.class_name, new_break.func_name, opline); + } + + if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], new_break.class_name, new_break.class_len, (void **)&class_table) == FAILURE) { + zend_hash_init(&class_breaks, 8, NULL, phpdbg_opline_class_breaks_dtor, 0); + zend_hash_update( + &PHPDBG_G(bp)[PHPDBG_BREAK_METHOD_OPLINE], + new_break.class_name, + new_break.class_len, + (void **)&class_breaks, sizeof(HashTable), (void **)&class_table); + } + + if (zend_hash_find(class_table, new_break.func_name, new_break.func_len, (void **)&method_table) == FAILURE) { + zend_hash_init(&method_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0); + zend_hash_update( + class_table, + new_break.func_name, + new_break.func_len, + (void **)&method_breaks, sizeof(HashTable), (void **)&method_table); + } + + if (zend_hash_index_exists(method_table, opline)) { + phpdbg_notice("Breakpoint already exists for %s::%s:%d", new_break.class_name, new_break.func_name, opline); + efree(new_break.func_name); + efree(new_break.class_name); + PHPDBG_G(bp_count)--; + return; + } + + zend_hash_index_update(method_table, new_break.opline, &new_break, sizeof(phpdbg_breakopline_t), NULL); +} + +PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, int opline TSRMLS_DC) /* {{{ */ +{ + phpdbg_breakopline_t new_break; + HashTable func_breaks, *func_table; + + new_break.func_len = strlen(function); + new_break.func_name = estrndup(function, new_break.func_len); + new_break.opline = opline; + new_break.id = PHPDBG_G(bp_count)++; + + if (phpdbg_resolve_opline_break(&new_break TSRMLS_CC) == FAILURE) { + phpdbg_notice("Pending breakpoint #%d at %s:%d", new_break.id, new_break.func_name, new_break.opline); + } else { + phpdbg_notice("Breakpoint #%d added at %s:%d", new_break.id, new_break.func_name, new_break.opline); + } + + if (zend_hash_find(&PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], new_break.func_name, new_break.func_len, (void **)&func_table) == FAILURE) { + zend_hash_init(&func_breaks, 8, NULL, phpdbg_opline_breaks_dtor, 0); + zend_hash_update( + &PHPDBG_G(bp)[PHPDBG_BREAK_FUNCTION_OPLINE], + new_break.func_name, + new_break.func_len, + (void **)&func_breaks, sizeof(HashTable), (void **)&func_table); + } + + if (zend_hash_index_exists(func_table, opline)) { + phpdbg_notice("Breakpoint already exists for %s:%d", new_break.func_len, opline); + efree(new_break.func_name); + PHPDBG_G(bp_count)--; + return; + } + + zend_hash_index_update(func_table, new_break.opline, &new_break, sizeof(phpdbg_breakopline_t), NULL); +} + PHPDBG_API void phpdbg_set_breakpoint_opcode(const char *name, size_t name_len TSRMLS_DC) /* {{{ */ { phpdbg_breakop_t new_break; diff --git a/phpdbg_bp.h b/phpdbg_bp.h index eb30e3ee25..0d72ea9028 100644 --- a/phpdbg_bp.h +++ b/phpdbg_bp.h @@ -51,6 +51,18 @@ typedef struct _phpdbg_breakmethod_t { int id; } phpdbg_breakmethod_t; +/** + * Breakpoint opline num based representation + */ +typedef struct _phpdbg_breakopline_t { + const char *class_name; + size_t class_len; + const char *func_name; + size_t func_len; + int opline; + int id; +} phpdbg_breakopline_t; + /** * Breakpoint opline based representation */ @@ -79,12 +91,20 @@ typedef struct _phpdbg_breakcond_t { int id; } phpdbg_breakcond_t; +PHPDBG_API void phpdbg_save_oplines(zend_op_array *op_array TSRMLS_DC); + +PHPDBG_API void phpdbg_resolve_op_array_breaks(zend_op_array *op_array TSRMLS_DC); +PHPDBG_API void phpdbg_resolve_op_array_break(phpdbg_breakopline_t *brake, zend_op_array *op_array TSRMLS_DC); +PHPDBG_API int phpdbg_resolve_opline_break(phpdbg_breakopline_t *new_break TSRMLS_DC); + PHPDBG_API void phpdbg_set_breakpoint_file(const char*, long TSRMLS_DC); PHPDBG_API void phpdbg_set_breakpoint_symbol(const char*, size_t TSRMLS_DC); PHPDBG_API void phpdbg_set_breakpoint_method(const char*, const char* TSRMLS_DC); PHPDBG_API void phpdbg_set_breakpoint_opcode(const char*, size_t TSRMLS_DC); PHPDBG_API void phpdbg_set_breakpoint_opline(zend_ulong TSRMLS_DC); PHPDBG_API void phpdbg_set_breakpoint_opline_ex(phpdbg_opline_ptr_t TSRMLS_DC); +PHPDBG_API void phpdbg_set_breakpoint_method_opline(const char *class, const char *method, int opline TSRMLS_DC); +PHPDBG_API void phpdbg_set_breakpoint_function_opline(const char *function, int opline TSRMLS_DC); PHPDBG_API void phpdbg_set_breakpoint_expression(const char*, size_t TSRMLS_DC); int phpdbg_find_breakpoint_file(zend_op_array* TSRMLS_DC); diff --git a/phpdbg_break.c b/phpdbg_break.c index 1b9b14b82a..cb2a01b5bd 100644 --- a/phpdbg_break.c +++ b/phpdbg_break.c @@ -59,6 +59,15 @@ PHPDBG_BREAK(address) /* {{{ */ phpdbg_set_breakpoint_opline(param->addr TSRMLS_CC); break; + case NUMERIC_METHOD_PARAM: + phpdbg_set_breakpoint_method_opline(param->method.class, param->method.name, param->num TSRMLS_CC); + break; + + case NUMERIC_PARAM: + case FILE_PARAM: + phpdbg_set_breakpoint_function_opline(param->str, param->num TSRMLS_CC); + break; + phpdbg_default_switch_case(); } diff --git a/phpdbg_cmd.c b/phpdbg_cmd.c index e9a81c9505..4f3fe71eed 100644 --- a/phpdbg_cmd.c +++ b/phpdbg_cmd.c @@ -35,8 +35,10 @@ PHPDBG_API const char *phpdbg_get_param_type(const phpdbg_param_t *param TSRMLS_ return "numeric"; case METHOD_PARAM: return "method"; + case NUMERIC_METHOD_PARAM: + return "method opline"; case FILE_PARAM: - return "file"; + return "file or function opline"; case STR_PARAM: return "string"; default: /* this is bad */ @@ -54,36 +56,44 @@ PHPDBG_API phpdbg_param_type phpdbg_parse_param(const char *str, size_t len, php } if (phpdbg_is_addr(str)) { - param->addr = strtoul(str, 0, 16); param->type = ADDR_PARAM; goto parsed; } else if (phpdbg_is_numeric(str)) { - param->num = strtol(str, NULL, 0); param->type = NUMERIC_PARAM; goto parsed; } else if (phpdbg_is_class_method(str, len+1, &class_name, &func_name)) { - param->method.class = class_name; param->method.name = func_name; param->type = METHOD_PARAM; goto parsed; - } else { - const char *line_pos = strchr(str, ':'); + char *line_pos = strrchr(str, ':'); if (line_pos && phpdbg_is_numeric(line_pos+1)) { - char path[MAXPATHLEN]; - - memcpy(path, str, line_pos - str); - path[line_pos - str] = 0; + param->len = line_pos - str; + param->str = estrndup(str, param->len); + + if (strchr(str, ':') == line_pos) { + *line_pos = 0; + param->file.name = phpdbg_resolve_path(param->str TSRMLS_CC); + param->num = param->file.line = strtol(line_pos+1, NULL, 0); + param->type = FILE_PARAM; + } else { + *line_pos = 0; + if (phpdbg_is_class_method(str, line_pos - str, &class_name, &func_name)) { + param->num = strtol(str, NULL, 0); + param->method.class = class_name; + param->method.name = func_name; + param->type = NUMERIC_METHOD_PARAM; + } else { + phpdbg_error("Impossible to parse %s", str); + } + } - param->file.name = phpdbg_resolve_path(path TSRMLS_CC); - param->file.line = strtol(line_pos+1, NULL, 0); - param->type = FILE_PARAM; goto parsed; } } diff --git a/phpdbg_cmd.h b/phpdbg_cmd.h index d72cb5b0bd..69d36828c3 100644 --- a/phpdbg_cmd.h +++ b/phpdbg_cmd.h @@ -37,7 +37,8 @@ typedef enum { FILE_PARAM, METHOD_PARAM, STR_PARAM, - NUMERIC_PARAM + NUMERIC_PARAM, + NUMERIC_METHOD_PARAM } phpdbg_param_type; typedef struct _phpdbg_input_t phpdbg_input_t;