> Even if -Og was ubiquitous, it is still suboptimal to -O0: it can still inline code a bit too aggressively for an effective debugging session. IMO, the problem is not so much the inlining, but the sad state of the optimization passes losing track of many things and producing useless debug info.
It's not just inlining. With -Og (clang or gcc), the debugger frequently can't even tell me the value of function arguments because they've been "optimized out".
Yes, the debugger might have "forgot" the value of that function argument. So check a few other levels of the call stack that it was passed through. Or a local variable might be unavailable in a given line. So have breakpoints a bit before, or trace it into the next function call.
And when all else fails, you can go and look at the disassembly. Yes, calling conventions are stupid, instruction mnemonics are a hassle and register aliases are terrible, but even with rudimentary understanding you can generally figure out what's going on. If your code does a multiplication, look for the `mult`. If your code calls some external function, look for a `call`. Compilers can do cool things with your code, but computers are not magic - you can actually just look at what your CPU does, and it will strongly correlate with your code. As an added bonus, this helps you actually understand what code your compiler produces, which (if you reach for C++!) you probably should care about to some degree.
The only reason I have to care about debug builds is code coverage and/or iterator-checked runs, but that's something that can run overnight if it has to.
/rant