mirror of
https://github.com/php/php-src.git
synced 2026-03-29 19:52:20 +02:00
break on class methods
don't dtor eval'd retval more reliable break on methods
This commit is contained in:
9
phpdbg.c
9
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[] = {
|
||||
|
||||
8
phpdbg.h
8
phpdbg.h
@@ -48,9 +48,10 @@
|
||||
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 */
|
||||
|
||||
83
phpdbg_bp.c
83
phpdbg_bp.c
@@ -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;
|
||||
} /* }}} */
|
||||
|
||||
|
||||
13
phpdbg_bp.h
13
phpdbg_bp.h
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
142
phpdbg_prompt.c
142
phpdbg_prompt.c
@@ -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 (!(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 (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 (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)));
|
||||
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));
|
||||
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)));
|
||||
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) {
|
||||
|
||||
16
test.php
16
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;
|
||||
?>
|
||||
|
||||
Reference in New Issue
Block a user