Buggin' Out with the Vim Debugger: Quirks, Tweaks n Tips (27 April 2008)

by Dan Peade | Permalink

explore : PHP, vim, php, debugger

This article assumes you have already experimented with using the vim debugger as described in this fine article. Big respect to Sueng Woo Shin for writing the debugger script itself. If you've already experimented with it and think you want to use it more then read on...

Using a debugger wins hands down over the good old echo/die approach but if you want to use the aforementioned debugger (which you do because you want to use vim right right!) then you may need to get used to a few little quirks and maybe even tweak it a little bit so feels a bit....righter.

So with that in mind here's a few little things that I've done and realised that have made it start to feel that little bit righter to me ...

1) How to debug a script which is invoked via the PHP CLI

To debug a page being served up via apache you point your browser to http://example.com/index.phpXDEBUG_SESSION_START=1 in order to set a cookie your browser and then the presence of this cookie indicates that you're keen to debug some stuff.

If the code you want to debug is being run via the PHP CLI then the above won't work so instead you need to set an environment variable before you run invoke the PHP CLI to run your script. This works for tcsh:

setenv "XDEBUG_CONFIG=vim=1"

Then run your script via the CLI (eg, php -f my_script.php) and as long as you have Vim open and have pressed F5 (like you normally do when you want to debug in vim) then you should be able to debug your script.

2) What's going on with regards to the files that get opened when a debugging session starts up?

Well regardless of if you like it or not the debugger always opens the" first" file in the call stack, if you like. So if you are debugging a handler that gets invoked from say http://workingsoftware.com.au/myapp/index.php?FindProduct then it always opens index.php. This is quite annoying cos most of the time you don't really want to debug index.php...in the above example you probably want to debug find_product.class.php, which is the file where the application code actually resides (whereas index.php is part of a framework and you're usually interested in debugging the application, not the framework).

There's no real way around this that I can see. So this is what I do

if say I want to debug find_product.class.php:

a)cd myapp/packages/myapp/handlers/

b)open find_product.class.php by at the command line doing:

vim find_product.class.php

c) hit F5

d) quickly (within the 5 second limit....see below - I have made this
limit longer) go to the URL in your browser (ie,
http://workingsoftware.com.au/myapp/index.php?FindProduct)

e) the debugger will open up index.php and in the process close your
find_product.class.php leaving you with index.php and the other
debugger specific windows. Bit annoying. Since you dont want to debug index.php close this file by doing :q

f) now you're left with just the debugger specific windows (actually
viewports) in vim. Now open up the file you want to actually debug,
find_product.class.php, by doing :sp find_product.class.php

g) Now go ahead and do your debugging. Once the debugger session
finishes you'll be left with the find_product.class.php file open in
vim. Now the good news is that if you set any breakpoints in your
find_product.class.php file while you were debugging, as long as you
don't close vim itself (doesn't matter if you close the
find_product.class.php file and reopen it, as long as vim stays
running) then you can hit F5 again to start another debugging session
and repeat the above steps and once you've gotten back round to this
step you will see that the breakpoints you set in the last debugging
session have been remembered in this one.

3) What are all these bloody windows?

I have found that using the debugger was made quite hokey due to the fact that the screen space got crowded out with a lot of superflous stuff. I can't see, at this stage, why you really need the TRACE_WINDOW or HELP_WINDOW to be there in every debugging session. The WATCH_WINDOW and the STACK_WINDOW are the only real useful ones.

So I have edited the debugger.py file to do away with these windows.

Now when you start up a debugging session you'll see the file(s) you're debugging, the WATCH_WINDOW and the STACK_WINDOW all stacked on top each other like you'd get if you did :sp normally.

The fine peeps who developed this debugger have mapped the F1 key so that it resizes the debugger windows. This is cool but I found you had to press F1 three times to get the window to resize the way I wanted it. So I edited debugger.py again to make it just toggle between the equivalent of Ctrl+W= and Ctrl+W_ . So when you're debugging if you are in the find_product.class.php file's window and you press F1 it will blow that window up to take up the majority of the screen. Press it again and it will go back to an even split between find_product.class.php, WATCH_WINDOW and STACK_WINDOW. Go into the WATCH_WINDOW window and press F1 and it will make the WATCH_WINDOW blow up, hit again adn back to even split. Same goes for STACK_WINDOW obviously.

If you want to try out this edited version of debugger.py you can download it here. All the original code has been more or less left in there, I've just commented the various bits out. You can run a diff on the original to see exactly what the changes are.

4) Controlling the debugger - keyboard commands

I realise this is probably stating the obvious for a lot of people and that it is also covered in the debugger script documentation but I'll include it anyway for completeness...Also if you're using the aforementioned edited version of debugger.py you will no longer have the HELP_WINDOW in your debugging session so you'll have to remember the commands with your brain...this might help:

F1: toggle resize

F2: step into. This will open up other files if need be. So say you
hit F2 on a line that calls Query::connect() then the debugger will
replace the current file with query.class.php (since that's where teh
code for the Query class resides). So you can continue debugging in
that file or you can use the "step out" command to skip out and go
back to where you can from. If the function being called is in the
same file then you'll just step into that function but in the file
will stay the same.

F3: step over. So if you're on the line that calls Query::connect()
and you hit F3 then it will go to next line in after that one instead
of opening up query.class.php. If its a function call to a function in
the same file then instead of stepping into that function you'll just
go to the next line.

F4: step out. If you've drilled down into query.class.php and you want
to skip back out to where you came from hit F4.

F5: run. This will run the code until it finishes or until it hits a breakpoint.

F6: this will end your debugging session

F11: dump context to WATCH_WINDOW. this will dump the values of *all* the variables in scope into the WATCH_WINDOW

F12: if you have the cursor positioned over a variable in the file
that you are debugging then hitting F12 will dump the value of that
variable out into the watch window. Two things to note about hitting
F12: a) if you hit F12 and the cursor ispositioned on an empty
line then it will cause vim to go into INSERT mode. Quite annoying but
good to know. b) if you hit F12 and the cursor is in the WATCH_WINDOW or the STACK_WINDOW then it will print "no commands none" and end yourdebugging session. Again annoying but good to know.

,e If you do ,e and then type in the name of some variable then you
will see that variable's value. If its an array you'll see the array
contents, if its an object you'll see the object's contents. if it's
empty or out of scope you'll see (null).

:B sets a breakpoint

:Bp unsets a breakpoint

4) A couple of handy commands for managing breakpoints

So you can use :B and :Bp to toggle a break point on/off. But some other useful stuff that's not immediately obvious is:

a)to list all breakpoints, showing which line each one is on:

:sign place

b)to clear all breakpoints:

:sign unplace *

c) to go to the next breakpoint:

...well I haven't figured this one out yet ;-)

5) Five seconds doesn't seem like long enough..

The debugger.py script is hard coded to wait 5 seconds for a connection after you've pressed F5. This didn't seem like long enough to me so I changed it to ait 15 seconds.

This change is in the edited debugger.py which is here. Just search for 'serv.settimeout(15);' and change 15 to however many seconds you think is reasonable if you want to change it.

So they're a few little things I found handy. If you find more let us know ;-)

Comments

hi just to add another usefull thing :Bp would set up a breakpoint with an expression according to context you might wanna check, http://slackdna.blogspot.com/search/label/vim-dbgpclient
- Zeft (2009-03-11)