View Issue Details

IDProjectCategoryView StatusLast Update
0001727XdebugRemote Debuggingpublic2020-01-14 21:16
Reporterjanw.meAssigned Toderick 
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version2.8.1 
Target Version2.9.1Fixed in Version2.9.1 
Summary0001727: Debugger stops more often than expected due to resolving breakpoints
DescriptionXdebug breaks on the function definition before it stops inside the function with a breakpoint.
I also Filled a bug with PHPStorm: https://youtrack.jetbrains.com/issue/WI-50152
The attached image explains it best.

It started happing last week, PHPstorm released an update, but a provision of the vagrant might also have updated the xdebug version, I don't know.
Additional InformationI run Ubuntu and using https://github.com/Varying-Vagrant-Vagrants/VVV for the dev enviroment.
PHP 7.3.12 inside the VVV
Xdebug 2.8.1
PhpStorm 2019.3 Build #PS-193.5233.101, built on November 27, 2019 no Non-Bundled Plugins.

TagsNo tags attached.
Operating SystemUbuntu + https://github.com/Varying-Vagrant-Vagrants/VVV
PHP Version7.3.10-7.3.14

Activities

janw.me

2019-12-09 09:23

reporter  

xdebug-test.php.png (21,439 bytes)
xdebug-test.php.png (21,439 bytes)
xdebug-remote.log (42,078 bytes)
xdebug-test.php (18 bytes)
x-debug ini settings.png (381,392 bytes)

derick

2019-12-12 17:35

administrator   ~0005207

I've added a comment at the JetBrains tracker, but I'm copying it in here, to have it all in one spot too:

Hi,

I had a look, and what @LazyOne writes is pretty much spot on, except for the point where you call it an "issue". This is a deliberate change to make breakpoints "stick" in case you inadvertently put them on a line where PHP doesn't have code generated for. The case is lined out in an [article](https://derickrethans.nl/breakpoints.html) that I wrote that about a year ago, and which was first implemented in Xdebug 2.8 (on request from JetBrains, FWIW).

I'll try to explain what happens. If you set `xdebug.remote_log_level` to `10`, the xdebug log file will corroborate what I say as well.

Line breakpoints are set with *filename* and *line* values, with the intention that Xdebug stops when code on that line gets executed. In some cases (as is explain in my article), PHP doesn't generate internal code on the lines that you expect. Xdebug, with the `breakpoint_resolve` feature on, will scan backwards and forwards from the given line to see whether it can find an executable line if there is not one on the *line* that the breakpoint has been set with.

Xdebug does this whenever a new function scope gets entered (which includes entering an included/required file). For this walk through, we look at PHP 7.3. With 7.4 this issue shows up less frequently, because it has optimised some more lines of (internal) code away.

Let's take this as file contents:

  1 <?php                            
  2 
  3 function non_called_function() {        
  4     $break = 1;                                                                                                   
  5     return '';
  6 }         
  7                                         
  8 function called_function() {                                                                                      
  9     $break = 2;                                                                                                   
 10     return'';                                                                                                     
 11 }                                                                                                                 
 12 
 13 called_function();


With breakpoints set on line `4` and `9`, when the main script is started, that scope (`{main}`) only has lines of executable code on line 3, 8, 13, and 14 as this VLD output shows:

filename:       /home/derick/dev/php/derickr-xdebug/tests/debugger/bug01727.inc
function name:  (null)
number of ops:  8
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   3     0  E >   EXT_STMT                                                 
   8     1        EXT_STMT                                                 
  13     2        EXT_STMT                                                 
         3        INIT_FCALL                                               'called_function'
         4        EXT_FCALL_BEGIN                                          
         5        DO_FCALL                                      0          
         6        EXT_FCALL_END                                            
  14     7      > RETURN                                                   1


As there is no executable line 4 or 9, the resolving mechanism starts at the given line, and scans forwards for a maximum of 5 lines. Which means that `4` becomes `8`, and `9` becomes `13`. After the resolving happened, Xdebug stops on line 8 (the first "break") as the breakpoint on `4` has now become `8`. On the second `run` the next stop is at line `13` because the breakpoint from `9` was resolved to `13`. On the third run, the PHP engine enters the `called_function` function where it re-resolves the breakpoint that you had originally set on line `9` back to `9`, as now PHP can see code there, as is illustrated by VLD again:

function name:  called_function
number of ops:  4
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   8     0  E >   EXT_NOP                                                  
   9     1        EXT_STMT                                                 
  10     2        EXT_STMT                                                 
         3      > RETURN  


And hence, it stops as a third time on line `9`, where you had expected that PHP/Xdebug would have stopped the first time.

The scanning of **5** lines forwards was picked as a compromise between not missing lines of code to be executed, and also to make sure that it wouldn't read "outside of the function". The latter seems to have happened here, because your functions are really short, and neither of them have a usually customary docblock either. I believe this is an outlier, as so much code in for example this Symfony file shows: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/DomCrawler/Crawler.php#L205 — as docblocks are usually at least 5 lines large, the situation that you ran into here is almost never a problem, although of course, an occasional false positive can occur.

Now the question is, does it warrant reducing the max scan lines from `5` to something shorter, with the added change to miss executable lines further from where you think they would have been, or not?

derick

2020-01-13 00:09

administrator   ~0005214

This is now fixed in the xdebug_2_9 branch in GIT. It would be lovely if you could try this out before I put it in a release!

rfay

2020-01-14 21:16

reporter   ~0005216

I confirm that this fix works, at least given the original report.

Using the code from the report in https://github.com/drud/ddev/issues/1996:

Ubuntu 18.04, php-dev (7.2)

* Checked out xdebug 2.8.1 and built it with rebuild.sh and repeated the issue as described.
* Checked out xdebug master (aabc2f504fe07fc2429aeee4f8ed8bd940bf013a) and built it with rebuild.sh and was no longer able to recreate the issue, and breakpoints behaved as expected.
* I didn't do a particularly extensive test, just tested this trivial section, but the problem appears to be resolved.

The xdebug version 3.0.0-dev of the master build did confuse me :)

Thanks for all the effort on this @derick ! And thanks for the years of effort maintaining a fundamental resource.

Issue History

Date Modified Username Field Change
2019-12-09 09:23 janw.me New Issue
2019-12-09 09:23 janw.me File Added: xdebug-test.php.png
2019-12-09 09:23 janw.me File Added: xdebug-remote.log
2019-12-09 09:23 janw.me File Added: xdebug-test.php
2019-12-09 09:23 janw.me File Added: x-debug ini settings.png
2019-12-12 17:35 derick Note Added: 0005207
2020-01-12 14:48 derick Summary PHPStrorm stops on function definition => Debugger stops more often than expected due to resolving breakpoints
2020-01-12 14:48 derick Target Version => 2.9.1
2020-01-13 00:09 derick Assigned To => derick
2020-01-13 00:09 derick Status new => closed
2020-01-13 00:09 derick Resolution open => fixed
2020-01-13 00:09 derick Fixed in Version => 2.9.1
2020-01-13 00:09 derick Note Added: 0005214
2020-01-14 21:16 rfay Note Added: 0005216