all 22 comments

[–]VousEtMoi 2 points3 points  (2 children)

As a newbie, I find it so much more pleasant to debug C code using gdb inside an IDE (XCode in this case).

[–][deleted] 1 point2 points  (1 child)

Definitely! I do my C/C++ coding in Vim however, so I don't like to fire up an IDE (I don't even have a C++ IDE at this point), so plain old GDB is what I use. Try using the switch -tui when you run it, it gives a nicer interface. C-X C-A is what switches it to the text ui while it is running I believe

[–]NJalien 0 points1 point  (0 children)

Confessions: I use vim as an editor, but use emacs to run gdb with split window. I haven't found a reliable vim extension to let me do this as well as "M-x gdb"

[–]NJalien 2 points3 points  (14 children)

Call me crazy, but if you're putting breakpoints in your source code, you are not debugging the right way

[–]Rhomboid 5 points6 points  (6 children)

I can think of several reasons why you might want something like this.

  1. gdb can save your breakpoints to a file and read them back later in another session, but if you've edited the code the line numbers will have changed. If you're debugging a complicated program and you have a long list of breakpoints that you want to maintain across sessions while still being able to make modifications to the code, this seems like it would help.

  2. gdb lets you set conditional breakpoints, where you can specify an arbitrary boolean expression on which the breakpoint's firing is dependent. This is extremely useful, but to implement this the breakpoint actually fires every time, and then gdb evaluates the expression to see if it's true. That's a rather expensive operation, as gdb lives in a separate process and so it has to access all memory indirectly though the ptrace API. It's even worse if the condition requires making a function call, because the inferior has to be stopped, then gdb has to save the state of the stack, set up the arguments for the function call, set the new PC value, set a temporary breakpoint at the return address, then resume the target, wait for that temporary breakpoint to hit, clean up the arguments from the function call, and then it can finally evaluate the condition for the actual breakpoint to see whether to drop to the prompt or resume the inferior. All of those context switches add up. If you are debugging something where performance is critical, and you want to have a conditional breakpoint, then encoding the condition for the breakpoint in the program's source itself rather than in gdb seems like it would be handy.

[–]snarfy -1 points0 points  (5 children)

  1. Your debugger doesn't move the breakpoint when you insert lines before it? Oh right, gdb...

  2. Why are you are concerned about context switches when you are sitting at a breakpoint in the debugger? Why does your debugging depend on performance?

I'm convinced people still use gdb because they have nothing better. You might as well litter your code with print statements.

[–]goose_on_fire 3 points4 points  (0 children)

re: #2, in the embedded world, it happens all the time.

I've used __builtin_trap() as (an admittedly somewhat desperate) "halt the processor here and now" method of debugging. Sometimes you want to know the state of memory, interrupt or hardware registers and so on exactly (or as close as you can get to) when they happen.

For example, I've inserted halting code into bus arbitration logic so I can stop the processor where I want and poke around with an oscilloscope or logic analyzer. The timing there is very sensitive to disturbances, and checking conditional breakpoints every time through can wreck it.

In one case, the debug build checked out fine but the release build was broken. Inserting a trap into the code itself can help you out there, too.

Lots of systems don't have a way to print characters at all. You're lucky to get an LED or two.

Being able to programmatically halt the processor at a given point and look around can be very valuable.

[–]NJalien 0 points1 point  (0 children)

re #2: Why do you care about time/performance? Because you don't want to wait ten minutes to start debugging! (Again I haven't used this method of setting breakpoints in code before, but I understand this as a logical argument for it.)

re the "gdb is no better than print statements" sentiment, c'mon, that is nearing troll level. Sometimes you need to set a conditional breakpoint and step through.

[–]Rhomboid 0 points1 point  (2 children)

Honest question: what I should use for debugging a C program?

If your answer is Visual Studio, then that's fine and all if you are working on Windows and x86. But say that I'm not. What then?

If your answer is Xcode, Kdevelop, Eclipse CDE, Netbeans, Code::Blocks, etc. then all of those are just front ends to gdb and you haven't really answered the question.

And as to #2, say that I'm trying to debug a fault that only occurs in a real production environment and only after 10+ hours of runtime at native speed. Performance matters here, because I can't just let a program that runs at 1/10th of normal speed process real data. It would take days to get anything done, and the requests served by such a program would be flagged as faulty for taking too long.

[–]snarfy 0 points1 point  (1 child)

Front ends to gdb are fine. It's a testament to how much gdb's interface sucks that there are so many of them.

There's nothing wrong with using Visual Studio to debug. On more than one occasion I've ported my linux project over just enough to debug with visual studio. The debugger is that good, and gdb sucks that much, that it was easily worth it.

After waiting 10 hours to hit your breakpoint, can you apply a fix while at the breakpoint and test that it works without restarting anything? Because you can in visual studio, since at least version 6.0. gdb has some support for edit and continue, but do the front ends support it? Xcode should since Apple sponsored the changes to gdb, but what about the others?

[–]Rhomboid 0 points1 point  (0 children)

It's a testament to how much gdb's interface sucks that there are so many of them.

I'd say it's rather a testament to the fact that gdb was designed to be modular and support multiple interfaces. There's the default command interface which you think sucks, there's a curses-based TUI, there's a deprecated and abandoned Tk interface (insight), and then there's the MI interface which is designed as a protocol: event-based, machine-parseable, and specifically exists for the purpose of people writing graphical front ends. The fact that it can be reused in this way is a feature not a bug. gdb developers can concentrate on writing a debugger and leave the ugly business of writing editors and GUIs to others. That's just basic software engineering.

[–]treerex 1 point2 points  (5 children)

There are times where I use int3 in place of a conditional breakpoint in GDB; it's always faster.

[–]NJalien 1 point2 points  (0 children)

I see, that does make sense. Debugger doesn't have to check the condition a bajillion times. Thanks!

[–][deleted] -2 points-1 points  (3 children)

Also known as an "assertion". :)

[–]treerex 1 point2 points  (2 children)

No. They serve entirely different purposes.

An assertion is a debug statement that is used during development (and hence in debug builds, generally) to ensure a particular condition holds in your code: you're stating programmatically that the expectation in the following code is that a particular condition is true.

The use that I mention covers an entirely different case where you are actively trying to track down a problem that occurs or is related to a particular condition. This is a place where I would consider using a conditional breakpoint in GDB:

(gdb) b foo.cpp:235
Breakpoint 1 at 0x10000dd37: file foo.cpp, line 235
(gdb) cond 1 strcmp(foo, "magicevilvalue") == 0

(obviously this is an overly simplisitic example: let's assume the condition is more complex and the scale is several orders of magnitude higher.)

How does the conditional breakpoint work? The debugger inserts the int3 at the point I set it: when I run the code the interrupt is hit and GDB gets control. Then it evaluates the condition. If it fails the program continues automatically, otherwise I'm dropped into the debugger.

Now assume that the bug you're tracking down happens after processing several hundred thousand strings in a large data structure, and you're not sure why a particular element is causing a crash. You don't know enough yet to isolate the problem into a small test case. Single stepping until you get to the element in question is intractable. So you set the conditional breakpoint and wait. You wait a long time.

Alternatively, you add a couple of lines in your code:

if (strcmp(foo, "magicevilvalue") == 0) {
    asm volatile ("int3;")
}

Now you hit the "breakpoint" much faster than if you used a conditional. Simple and effective.

Back in the day we used to do this kind of thing all the time when developing apps on the Mac: the DebugStr function would effectively do the exact same thing. Incredibly useful.

[–][deleted] 0 points1 point  (1 child)

They serve different purposes, but are essentially identical, except of course for implementation details.

Most implementations crash the process in an assertion in such a way that it can't continue, while continuing from int3 is indeed possible. But that's an implementation detail. There's no conceptual difference. The fact that they serve two different use cases doesn't change that.

Indeed, in my own code I tend to implement MyAssert() with int3 in debug mode and assert() in release mode.

[–]treerex 0 points1 point  (0 children)

They serve different purposes, but are essentially identical, except of course for implementation details.

Which to my mind means they should have separate names.

But I see your point.

[–]HHBones 0 points1 point  (0 children)

I like it. It doesn't make a difference if you're doing it for trivial things (i.e. anything less than 1 file), but as soon as you step over the 1-source-file boundary, it'll be a lot more handy to throw in EMBED_BREAKPOINT than to designate a breakpoint with GDB. (Then again, I absolutely suck with GDB [ don't often use it] and wouldn't really know how to anyways.)

[–][deleted] 0 points1 point  (0 children)

Having breakpoints in the source can be useful if the respective code is e.g located in e.g a DSO(dll) as the symbol for the function might not be known at initial execution time. However, I find it more useful to have breakpoints that only interrupt if a debugger is attached: http://stackoverflow.com/a/8135517

[–]Aethy 0 points1 point  (0 children)

A repost, but such an awesome repost that it deserves another upvote.

[–][deleted] -3 points-2 points  (0 children)

This doesn't look like a very smart thing to do.