Skip to content
Permalink
Browse files

Resolve breakpoints immediately when they're set

So that if the breakpoint is set in the scope that is currently executing, it
will also get resolved correctly.
  • Loading branch information...
derickr committed May 12, 2019
1 parent aa6c50d commit 10894fcb478e4cc382ca9e1e0115ff5fc02a8e0f
Showing with 118 additions and 51 deletions.
  1. +4 −1 tests/bug01388-02.phpt
  2. +4 −1 tests/bug01388-03.phpt
  3. +6 −0 tests/bug01388-18.inc
  4. +48 −0 tests/bug01388-18.phpt
  5. +56 −49 xdebug_handler_dbgp.c
@@ -34,7 +34,10 @@ dbgpRunFile( $filename, $commands );


-> breakpoint_set -i 3 -t line -n 4 -> breakpoint_set -i 3 -t line -n 4
<?xml version="1.0" encoding="iso-8859-1"?> <?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="breakpoint_set" transaction_id="3" id="" resolved="unresolved"></response> <notify xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" name="breakpoint_resolved"><breakpoint type="line" resolved="resolved" filename="file://bug01388-02.inc" lineno="10" state="enabled" hit_count="0" hit_value="0" id=""></breakpoint></notify>

<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="breakpoint_set" transaction_id="3" id="" resolved="resolved"></response>


-> run -i 4 -> run -i 4
<?xml version="1.0" encoding="iso-8859-1"?> <?xml version="1.0" encoding="iso-8859-1"?>
@@ -40,7 +40,10 @@ dbgpRunFile( $filename, $commands );


-> breakpoint_set -i 4 -t line -n 13 -> breakpoint_set -i 4 -t line -n 13
<?xml version="1.0" encoding="iso-8859-1"?> <?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="breakpoint_set" transaction_id="4" id="" resolved="unresolved"></response> <notify xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" name="breakpoint_resolved"><breakpoint type="line" resolved="resolved" filename="file://bug01388-03.inc" lineno="20" state="enabled" hit_count="0" hit_value="0" id=""></breakpoint></notify>

<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="breakpoint_set" transaction_id="4" id="" resolved="resolved"></response>


-> run -i 5 -> run -i 5
<?xml version="1.0" encoding="iso-8859-1"?> <?xml version="1.0" encoding="iso-8859-1"?>
@@ -0,0 +1,6 @@
<?php
$a = array( // breakpoint
1 => "first",
2 => "second",
);
?>
@@ -0,0 +1,48 @@
--TEST--
Test for bug #1388: Resolved Breakpoint: resolved with breakpoint set in current scope
--SKIPIF--
<?php
require __DIR__ . '/utils.inc';
check_reqs('dbgp');
?>
--FILE--
<?php
require 'dbgp/dbgpclient.php';
$filename = dirname(__FILE__) . '/bug01388-18.inc';
$commands = array(
'feature_set -n resolved_breakpoints -v 1',
'step_into',
'breakpoint_set -t line -n 2',
'run',
'detach',
);
dbgpRunFile( $filename, $commands );
?>
--EXPECT--
<?xml version="1.0" encoding="iso-8859-1"?>
<init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" fileuri="file://bug01388-18.inc" language="PHP" xdebug:language_version="" protocol_version="1.0" appid="" idekey=""><engine version=""><![CDATA[Xdebug]]></engine><author><![CDATA[Derick Rethans]]></author><url><![CDATA[https://xdebug.org]]></url><copyright><![CDATA[Copyright (c) 2002-2099 by Derick Rethans]]></copyright></init>

-> feature_set -i 1 -n resolved_breakpoints -v 1
<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="feature_set" transaction_id="1" feature="resolved_breakpoints" success="1"></response>

-> step_into -i 2
<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="step_into" transaction_id="2" status="break" reason="ok"><xdebug:message filename="file://bug01388-18.inc" lineno="3"></xdebug:message></response>

-> breakpoint_set -i 3 -t line -n 2
<?xml version="1.0" encoding="iso-8859-1"?>
<notify xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" name="breakpoint_resolved"><breakpoint type="line" resolved="resolved" filename="file://bug01388-18.inc" lineno="3" state="enabled" hit_count="0" hit_value="0" id=""></breakpoint></notify>

<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="breakpoint_set" transaction_id="3" id="" resolved="resolved"></response>

-> run -i 4
<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="run" transaction_id="4" status="stopping" reason="ok"></response>

-> detach -i 5
<?xml version="1.0" encoding="iso-8859-1"?>
<response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="detach" transaction_id="5" status="stopping" reason="ok"></response>
@@ -58,6 +58,7 @@
ZEND_EXTERN_MODULE_GLOBALS(xdebug) ZEND_EXTERN_MODULE_GLOBALS(xdebug)
static char *create_eval_key_file(char *filename, int lineno); static char *create_eval_key_file(char *filename, int lineno);
static char *create_eval_key_id(int id); static char *create_eval_key_id(int id);
static void line_breakpoint_resolve_helper(xdebug_con *context, function_stack_entry *fse, xdebug_brk_info *brk_info);


/***************************************************************************** /*****************************************************************************
** Constants and strings for statii and reasons ** Constants and strings for statii and reasons
@@ -839,7 +840,6 @@ DBGP_FUNC(breakpoint_set)
xdebug_brk_info *brk_info; xdebug_brk_info *brk_info;
char *tmp_name; char *tmp_name;
size_t new_length = 0; size_t new_length = 0;
function_stack_entry *fse;
XDEBUG_STR_SWITCH_DECL; XDEBUG_STR_SWITCH_DECL;


brk_info = xdmalloc(sizeof(xdebug_brk_info)); brk_info = xdmalloc(sizeof(xdebug_brk_info));
@@ -905,7 +905,8 @@ DBGP_FUNC(breakpoint_set)


/* If no filename is given, we use the current one */ /* If no filename is given, we use the current one */
if (!CMD_OPTION_SET('f')) { if (!CMD_OPTION_SET('f')) {
fse = xdebug_get_stack_tail(TSRMLS_C); function_stack_entry *fse = xdebug_get_stack_tail(TSRMLS_C);

if (!fse) { if (!fse) {
RETURN_RESULT(XG(status), XG(reason), XDEBUG_ERROR_STACK_DEPTH_INVALID); RETURN_RESULT(XG(status), XG(reason), XDEBUG_ERROR_STACK_DEPTH_INVALID);
} else { } else {
@@ -939,6 +940,14 @@ DBGP_FUNC(breakpoint_set)
} }
xdfree(tmp_name); xdfree(tmp_name);
xdebug_llist_insert_next(context->line_breakpoints, XDEBUG_LLIST_TAIL(context->line_breakpoints), (void*) brk_info); xdebug_llist_insert_next(context->line_breakpoints, XDEBUG_LLIST_TAIL(context->line_breakpoints), (void*) brk_info);

if (XG(context).resolved_breakpoints) {
function_stack_entry *fse = xdebug_get_stack_tail(TSRMLS_C);

if (fse) {
line_breakpoint_resolve_helper(context, fse, brk_info);
}
}
} else } else


if ((strcmp(CMD_OPTION_CHAR('t'), "call") == 0) || (strcmp(CMD_OPTION_CHAR('t'), "return") == 0)) { if ((strcmp(CMD_OPTION_CHAR('t'), "call") == 0) || (strcmp(CMD_OPTION_CHAR('t'), "return") == 0)) {
@@ -2689,129 +2698,127 @@ static void function_breakpoint_resolve_helper(void *rctxt, xdebug_brk_info *brk
} }
} }


static void line_breakpoint_resolve_helper(void *rctxt, xdebug_brk_info *brk_info, xdebug_hash_element *he) static void line_breakpoint_resolve_helper(xdebug_con *context, function_stack_entry *fse, xdebug_brk_info *brk_info)
{ {
xdebug_dbgp_resolve_context *ctxt = (xdebug_dbgp_resolve_context*) rctxt;

/* If the breakpoint's line number isn't in the function's range, bail out */ /* If the breakpoint's line number isn't in the function's range, bail out */
if (brk_info->original_lineno < ctxt->fse->op_array->line_start || brk_info->original_lineno > ctxt->fse->op_array->line_end) { if (brk_info->original_lineno < fse->op_array->line_start || brk_info->original_lineno > fse->op_array->line_end) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "R: Line number (%d) out of range (%d-%d)\n", brk_info->original_lineno, ctxt->fse->op_array->line_start, ctxt->fse->op_array->line_end); context->handler->log(XDEBUG_LOG_DEBUG, "R: Line number (%d) out of range (%d-%d)\n", brk_info->original_lineno, fse->op_array->line_start, fse->op_array->line_end);
return; return;
} }


/* If we have resolved before, we only resolve again if the current scope's /* If we have resolved before, we only resolve again if the current scope's
* line-span is *smaller* then the one that was used for the already * line-span is *smaller* then the one that was used for the already
* resolved case */ * resolved case */
if (brk_info->resolved == XDEBUG_BRK_RESOLVED && !function_span_is_smaller_than_resolved_span(ctxt->fse->op_array, &brk_info->resolved_span)) { if (brk_info->resolved == XDEBUG_BRK_RESOLVED && !function_span_is_smaller_than_resolved_span(fse->op_array, &brk_info->resolved_span)) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "R: Resolved span (%d-%d) is not smaller than function span (%d-%d)\n", brk_info->resolved_span.start, brk_info->resolved_span.end, ctxt->fse->op_array->line_start, ctxt->fse->op_array->line_end); context->handler->log(XDEBUG_LOG_DEBUG, "R: Resolved span (%d-%d) is not smaller than function span (%d-%d)\n", brk_info->resolved_span.start, brk_info->resolved_span.end, fse->op_array->line_start, fse->op_array->line_end);
return; return;
} else if (brk_info->resolved != XDEBUG_BRK_RESOLVED) { } else if (brk_info->resolved != XDEBUG_BRK_RESOLVED) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "I: Has not been resolved yet\n"); context->handler->log(XDEBUG_LOG_DEBUG, "I: Has not been resolved yet\n");
} else { } else {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "I: Resolved span (%d-%d) is smaller than function span (%d-%d)\n", brk_info->resolved_span.start, brk_info->resolved_span.end, ctxt->fse->op_array->line_start, ctxt->fse->op_array->line_end); context->handler->log(XDEBUG_LOG_DEBUG, "I: Resolved span (%d-%d) is smaller than function span (%d-%d)\n", brk_info->resolved_span.start, brk_info->resolved_span.end, fse->op_array->line_start, fse->op_array->line_end);
} }


/* If we're not in a normal function or method: */ /* If we're not in a normal function or method: */
if (XDEBUG_IS_NORMAL_FUNCTION(&ctxt->fse->function)) { if (XDEBUG_IS_NORMAL_FUNCTION(&fse->function)) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "I: '%s' is a normal function or method (%02x)\n", ctxt->fse->function.function, ctxt->fse->function.type); context->handler->log(XDEBUG_LOG_DEBUG, "I: '%s' is a normal function or method (%02x)\n", fse->function.function, fse->function.type);


/* If the 'line' breakpoint's file and current file don't match, bail out */ /* If the 'line' breakpoint's file and current file don't match, bail out */
if (strcmp(brk_info->file, STR_NAME_VAL(ctxt->fse->op_array->filename)) != 0) { if (strcmp(brk_info->file, STR_NAME_VAL(fse->op_array->filename)) != 0) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "R: Breakpoint file name (%s) does not match function's file name (%s)\n", brk_info->file, STR_NAME_VAL(ctxt->fse->op_array->filename)); context->handler->log(XDEBUG_LOG_DEBUG, "R: Breakpoint file name (%s) does not match function's file name (%s)\n", brk_info->file, STR_NAME_VAL(fse->op_array->filename));
return; return;
} }
} }
/* else, if we're in an eval: */ /* else, if we're in an eval: */
else if (ctxt->fse->function.type == XFUNC_EVAL) { else if (fse->function.type == XFUNC_EVAL) {
char *key, *dbgp_eval_key; char *key, *dbgp_eval_key;
xdebug_eval_info *ei; xdebug_eval_info *ei;


ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "I: Current 'function' is an eval statement\n"); context->handler->log(XDEBUG_LOG_DEBUG, "I: Current 'function' is an eval statement\n");


key = create_eval_key_file(ctxt->fse->filename, ctxt->fse->lineno); key = create_eval_key_file(fse->filename, fse->lineno);
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, " I: Looking up eval ID for '%s'\n", key); context->handler->log(XDEBUG_LOG_DEBUG, " I: Looking up eval ID for '%s'\n", key);
if (!xdebug_hash_find(ctxt->context->eval_id_lookup, key, strlen(key), (void *) &ei)) { if (!xdebug_hash_find(context->eval_id_lookup, key, strlen(key), (void *) &ei)) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, " R: Eval ID not found\n"); context->handler->log(XDEBUG_LOG_DEBUG, " R: Eval ID not found\n");
xdfree(key); xdfree(key);
return; return;
} }
xdfree(key); xdfree(key);


ctxt->context->handler->log(XDEBUG_LOG_DEBUG, " I: Constructing 'filename' for eval ID '%d'\n", ei->id); context->handler->log(XDEBUG_LOG_DEBUG, " I: Constructing 'filename' for eval ID '%d'\n", ei->id);
dbgp_eval_key = xdebug_sprintf("dbgp://%d", ei->id); dbgp_eval_key = xdebug_sprintf("dbgp://%d", ei->id);


if (strcmp(dbgp_eval_key, brk_info->file) != 0) { if (strcmp(dbgp_eval_key, brk_info->file) != 0) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, " R: Breakpoint file name (%s) does not match eval's file name (%s)\n", brk_info->file, dbgp_eval_key); context->handler->log(XDEBUG_LOG_DEBUG, " R: Breakpoint file name (%s) does not match eval's file name (%s)\n", brk_info->file, dbgp_eval_key);
xdfree(dbgp_eval_key); xdfree(dbgp_eval_key);
return; return;
} }
xdfree(dbgp_eval_key); xdfree(dbgp_eval_key);
} }
/* else, if we're an include or require: */ /* else, if we're an include or require: */
else if (ctxt->fse->function.type & XFUNC_INCLUDES) { else if (fse->function.type & XFUNC_INCLUDES) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "I: Current 'function' is a file scope (%s)\n", STR_NAME_VAL(ctxt->fse->op_array->filename)); context->handler->log(XDEBUG_LOG_DEBUG, "I: Current 'function' is a file scope (%s)\n", STR_NAME_VAL(fse->op_array->filename));


/* If the 'line' breakpoint's file and current file don't match, bail out */ /* If the 'line' breakpoint's file and current file don't match, bail out */
if (strcmp(brk_info->file, STR_NAME_VAL(ctxt->fse->op_array->filename)) != 0) { if (strcmp(brk_info->file, STR_NAME_VAL(fse->op_array->filename)) != 0) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, " R: Breakpoint file name (%s) does not match file's name (%s)\n", brk_info->file, STR_NAME_VAL(ctxt->fse->op_array->filename)); context->handler->log(XDEBUG_LOG_DEBUG, " R: Breakpoint file name (%s) does not match file's name (%s)\n", brk_info->file, STR_NAME_VAL(fse->op_array->filename));
return; return;
} }
} else { } else {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "R: We don't handle this function type (%02x) yet\n", ctxt->fse->function.type); context->handler->log(XDEBUG_LOG_DEBUG, "R: We don't handle this function type (%02x) yet\n", fse->function.type);
return; return;
} }


/* If the breakpoint's line number is in the set, mark as resolved */ /* If the breakpoint's line number is in the set, mark as resolved */
if (xdebug_set_in(ctxt->executable_lines, brk_info->original_lineno)) { if (xdebug_set_in(get_executable_lines_from_oparray(fse), brk_info->original_lineno)) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "F: Breakpoint line (%d) found in set of executable lines\n", brk_info->original_lineno); context->handler->log(XDEBUG_LOG_DEBUG, "F: Breakpoint line (%d) found in set of executable lines\n", brk_info->original_lineno);
brk_info->resolved_lineno = brk_info->original_lineno; brk_info->resolved_lineno = brk_info->original_lineno;
brk_info->resolved_span.start = ctxt->fse->op_array->line_start; brk_info->resolved_span.start = fse->op_array->line_start;
brk_info->resolved_span.end = ctxt->fse->op_array->line_end; brk_info->resolved_span.end = fse->op_array->line_end;
brk_info->resolved = XDEBUG_BRK_RESOLVED; brk_info->resolved = XDEBUG_BRK_RESOLVED;
xdebug_dbgp_resolved_breakpoint_notification(ctxt->context, brk_info); xdebug_dbgp_resolved_breakpoint_notification(context, brk_info);
return; return;
} else { } else {
int tmp_lineno; int tmp_lineno;


ctxt->context->handler->log(XDEBUG_LOG_DEBUG, "I: Breakpoint line (%d) NOT found in set of executable lines\n", brk_info->original_lineno); context->handler->log(XDEBUG_LOG_DEBUG, "I: Breakpoint line (%d) NOT found in set of executable lines\n", brk_info->original_lineno);


/* Check for a following line in the function */ /* Check for a following line in the function */
tmp_lineno = brk_info->original_lineno; tmp_lineno = brk_info->original_lineno;
do { do {
tmp_lineno++; tmp_lineno++;


if (xdebug_set_in(ctxt->executable_lines, tmp_lineno)) { if (xdebug_set_in(get_executable_lines_from_oparray(fse), tmp_lineno)) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, " F: Line (%d) in set (with span: %d-%d)\n", tmp_lineno, ctxt->fse->op_array->line_start, ctxt->fse->op_array->line_end); context->handler->log(XDEBUG_LOG_DEBUG, " F: Line (%d) in set (with span: %d-%d)\n", tmp_lineno, fse->op_array->line_start, fse->op_array->line_end);


brk_info->resolved_lineno = tmp_lineno; brk_info->resolved_lineno = tmp_lineno;
brk_info->resolved_span.start = ctxt->fse->op_array->line_start; brk_info->resolved_span.start = fse->op_array->line_start;
brk_info->resolved_span.end = ctxt->fse->op_array->line_end; brk_info->resolved_span.end = fse->op_array->line_end;
brk_info->resolved = XDEBUG_BRK_RESOLVED; brk_info->resolved = XDEBUG_BRK_RESOLVED;
xdebug_dbgp_resolved_breakpoint_notification(ctxt->context, brk_info); xdebug_dbgp_resolved_breakpoint_notification(context, brk_info);
return; return;
} else { } else {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, " I: Line (%d) not in set\n", tmp_lineno); context->handler->log(XDEBUG_LOG_DEBUG, " I: Line (%d) not in set\n", tmp_lineno);
} }
} while (tmp_lineno < ctxt->fse->op_array->line_end && (tmp_lineno < brk_info->original_lineno + 10)); } while (tmp_lineno < fse->op_array->line_end && (tmp_lineno < brk_info->original_lineno + 10));


/* Check for a previous line in the function */ /* Check for a previous line in the function */
tmp_lineno = brk_info->original_lineno; tmp_lineno = brk_info->original_lineno;
do { do {
tmp_lineno--; tmp_lineno--;


if (xdebug_set_in(ctxt->executable_lines, tmp_lineno)) { if (xdebug_set_in(get_executable_lines_from_oparray(fse), tmp_lineno)) {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, " F: Line (%d) in set\n", tmp_lineno); context->handler->log(XDEBUG_LOG_DEBUG, " F: Line (%d) in set\n", tmp_lineno);


brk_info->resolved_lineno = tmp_lineno; brk_info->resolved_lineno = tmp_lineno;
brk_info->resolved_span.start = ctxt->fse->op_array->line_start; brk_info->resolved_span.start = fse->op_array->line_start;
brk_info->resolved_span.end = ctxt->fse->op_array->line_end; brk_info->resolved_span.end = fse->op_array->line_end;
brk_info->resolved = XDEBUG_BRK_RESOLVED; brk_info->resolved = XDEBUG_BRK_RESOLVED;
xdebug_dbgp_resolved_breakpoint_notification(ctxt->context, brk_info); xdebug_dbgp_resolved_breakpoint_notification(context, brk_info);
return; return;
} else { } else {
ctxt->context->handler->log(XDEBUG_LOG_DEBUG, " I: Line (%d) not in set\n", tmp_lineno); context->handler->log(XDEBUG_LOG_DEBUG, " I: Line (%d) not in set\n", tmp_lineno);
} }
} while (tmp_lineno > ctxt->fse->op_array->line_start && (tmp_lineno > brk_info->original_lineno - 10)); } while (tmp_lineno > fse->op_array->line_start && (tmp_lineno > brk_info->original_lineno - 10));
} }
} }


@@ -2862,7 +2869,7 @@ static void breakpoint_resolve_helper(void *rctxt, xdebug_hash_element *he)
switch (brk_info->brk_type) { switch (brk_info->brk_type) {
case XDEBUG_BREAKPOINT_TYPE_LINE: case XDEBUG_BREAKPOINT_TYPE_LINE:
case XDEBUG_BREAKPOINT_TYPE_CONDITIONAL: case XDEBUG_BREAKPOINT_TYPE_CONDITIONAL:
line_breakpoint_resolve_helper(rctxt, brk_info, he); line_breakpoint_resolve_helper(ctxt->context, ctxt->fse, brk_info);
return; return;


case XDEBUG_BREAKPOINT_TYPE_CALL: case XDEBUG_BREAKPOINT_TYPE_CALL:

0 comments on commit 10894fc

Please sign in to comment.
You can’t perform that action at this time.