if you want to make use of visual studio's fairly awesome debugging capabilities, make sure your project is set up for incremental compilation, function level compilation, and lots of other random edit+continue junk that is all over the place that keeps on randomly being disabled at random.
Once your project is set up, its basically just a case of 'select debug build, press f5'.
If you've got a debugger attached, and this applies for gdb or msvc, you probably want to run quake windowed. This is especially true for certain d3d api versions/options...
Once your program crashes, it'll take you to the line it crashed at.
If you actually found all the right options mentioned in the first paragraph, you can actually make your fix/changes, press alt+f10, then press f5, and it'll continue from where it crashed, but with your changes in place, without having to restart the entire damn thing! Seriously awesome!
There's no real reason to run it outside of a debugger, other than the fact that it runs a little slower (due to being a debug build). If your bottleneck is opengl then it won't really be visible anyway (just starting the ide first will, and the debugger loading debugging info from dlls).
alt+f10: apply changes
f11: single step onto the next line (entering functions)
f10: single step onto the next line (don't step through called functions).
f9: toggle breakpoint on the current line
Really all you need.
FTE's svn has some extra minidump code in client/sys_win.c which can generate a dump file if it crashes on someone else's computer (its ifdefed - disabled in release builds). You can then open this file up on your computer and see the state of things as they were when they crashed on the other guy's computer. It can be quite useful, but it does require code changes to your engine in order to generate them (also requires external files to be unchanged from when the exe was written, the .pdb specifically, I think).
If you're debugging with gdb, make sure you compiled it with -g, preferably with -O0, and definitely not with -s (and not used the strip command either).
gdb --args myexe mycommandline
run: start running
next: single step (ignore function calls)
step: single step (step into functions)
info regi: register dump for debugging gcc bugs
bt: simple backtrace
bt full: backtrace that is actually useful
quit: give up and run away
break func: set a breakpoint on the first instruction of a function
break file line: set a breakpoint on a given file+line
watch variable: set a watchpoint on a variable
A simplistic aproach is to run it, let it crash, type bt full, then quit...
You can make a .gdbinit file containing commands to be run when gdb is started up, including 'set args=-window -game crashy -basedir myquakedir', 'file mybinary', 'run'.
If you're debugging on linux, I personally find that running it in valgrind is seriously awesome, although not without issues - specifically, ones caused by your graphics drivers being buggy shit. Use environment settings to configure indirect rendering and its much faster+sane, although you may loose certain GL extensions, so might impact debugging renderer changes.
Even if you think your code is fine, running it in valgrind often reveals some dirty little secrets. Just make sure they're *your* secrets before you start trying to fix them. :)
If you're using gcc to compile, there's an application called addr2line, which is a much faster alternative to what you currently do, and gives line numbers rather than just function names. Requires debugging info, the same as gdb does though.