1
0
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:
krakjoe
2013-11-12 00:27:48 +00:00
parent d61fa4cc52
commit e0cbd92fa4
7 changed files with 214 additions and 60 deletions

View File

@@ -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[] = {

View File

@@ -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 */

View 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;
} /* }}} */

View 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);

View 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");

View 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 (!(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) {

View File

@@ -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;
?>