Best way to print information when debugging a race condition - c

I am debugging an application to fix a segmentation fault that I suspect to be caused by a race condition.
I'd like to put some print statements in the code, but I know for experience that adding calls to printf is not recommended since this could change the behavior of the threads and in some case hide the bug.
Looking at other options, I have seen that with gdb it is possible to use break points to print something and then automatically continue the execution:
break foo
commands
silent
printf "Called foo: x is %d\n",x
cont
end
Is this any better then putting a printf in my code?
I know that gdb has a also Tracepoints but they only work with gdbserver and this is an additional level of complication that I would prefer to avoid at the moment.
Additional information: the application is written in C and it runs on Linux.

Is this any better then putting a printf in my code?
No, it's much worse. Every breakpoint that is hit in GDB triggers the following chain of events:
context switch from running thread to GDB
GDB stops all other threads (assuming default all-stop mode)
GDB evaluates breakpoint commands
GDB resumes all threads (this is itself a complicated multi-step process, which I would not go into here).
This is at least an order of magnitude more expensive and disruptive than a simple printf call, and is very likely to hide whatever race you were trying to debug.
The bottom line is that GDB is in general completely unsuitable for debugging data races.
I second the ThreadSanitizer recommendation by Christopher Ian Stern.
The only problem with this bug is that I am doing the debug on a production machine where I cannot install other SW.
First, ThreadSanitizer instruments your existing program. It has a runtime library, but that could be statically linked into your binary. There is nothing that you need to install on your production machine.
Second, ThreadSanitizer detects data races even when they do not cause visible behavior changes. It may well turn out that you don't need to run on your production machine at all: simply running your tests (you do have tests, right?) on your development machine may prove to be sufficient.

Since you are on Linux, I would recomend ThreadSanitizer. That is using a recent version of gcc or clang and passing -fsanitize=thread to the build. This isn't a printf repacment but should tell you explicitly about any race conditions in your code. Even after you solve this problem if you are working with multithreaded code, you will want to have this tool available. Alternately, or in addition, I have had good results with Valgrind's http://valgrind.org Data Race Detector, but I would start with ThreadSanitizer.

Related

Program does not segfault in gdb [duplicate]

I have a multithreaded C program, which consistently generates a segmentation fault at a specific point in the program. When I run it with gdb, no fault is shown. Can you think of any reason why the fault might occur only when not using the debugger? It's pretty annoying not being able to use it to find the problem!
Classic Heisenbug. From Wikipedia:
Time can also be a factor in heisenbugs. Executing a program under control of a debugger can change the execution timing of the program as compared to normal execution. Time-sensitive bugs such as race conditions may not reproduce when the program is slowed down by single-stepping source lines in the debugger. This is particularly true when the behavior involves interaction with an entity not under the control of a debugger, such as when debugging network packet processing between two machines and only one is under debugger control.
The debugger may be changing timing, and hiding a race condition.
On Linux, GDB also disables address space randomization, and your crash may be specific to address space layout. Try (gdb) set disable-randomization off.
Finally, ulimit -c unlimited and post-mortem debugging (already suggested by Robie) may work.
Perhaps when using gdb memory is mapped in a location which your over/under flow doesn't trample on memory that causes a crash. Or it could be a race condition that is no longer getting tripped. Although it sounds unintuitive, you should be happy your program was nice enough to crash on you.
Some suggestions
Try a static code analyzer such as the free
cppcheck
Try a malloc() debugger like
libefence
Try running it through valgrind
By debugging it you are changing the environment that it is running in. It sounds like you are dealing with some sort of race condition, and by debugging it things are scheduled slightly differently so you don't encounter the issue. That, or things are being stored in a slightly different way so it doesn't occur. Are you able to put some debugging output in the code to assist in figuring out the problem? That may have less of an impact and allow you to find your issue.
I have totally had this problem before! It was a race condition, and when I was stepping though the code with a debugger the thread i was in was slow enough to not trigger the race condition. Pretty awful.
If you're using gcc, try using the -Wall option to get all warnings. If you use an IDE like Eclipse, it would do that automatically.

Stepping into swapcontext() with gdb

I'm doing a project that makes unix system calls. To be specific, my project heavily relies of calls to getcontext(), makecontext(), swapcontext() and setcontext(). I tried to debug my code using gdb. I stepped into the code line by line and examined the control but once a call to swapcontext() is made, it doesn't step into the code anymore. Instead, debugging almost stops there and the rest of the program is run automatically instead of line by line. I guess gdb does not go into context calls? Is there any way to get around this? Is there a debugger that i can use for this?
Thanks
setcontext and swapcontext calls change the program's stack, and gdb gets confused. I do not know whether some other debugger can handle it nicely.
gdb steps through one thread and calls this the current thread. Other threads will run as you are doing this. If you set a breakpoint that gets hit in a thread other than the current thread then gdb will change the current thread to that thread. Stepping is now relative to the new current thread.
Stepping with gdb over calls of swapcontext() with 'step' or 'next' does not work because not only the stackpointer changes but also the call returns to a different code line (which is the desired effect of swapcontext()). As gdb puts a breakpoint in the next code line which will not be executed until another swapcontext() returns to this place the execution will not break.
You need to foresee the line to which swapcontext() will return and set a breakpoint there. For a new (unused) context this will be the line you specified as entry function. For used contexts it will probably one of the lines after a swapcontext() there...
You can repeatedly use GDB's stepi command to first step into and then step through the swapcontext() function. You must step several dozen times, including a few steps over kernel system calls—I presume to save the floating point state?—and you'll eventually come out in the user thread you're swapping to. It's a tad time-consuming, but it works.
As much as you may not like this answer, the best bet is to step through your code in small chunks by hand. Threaded programs don't play very well with debuggers like GDB and Valgrind (at least in my experience) and most bugs can be determined by a careful step-by-step manual analysis of the code.

fatal error disappeared when running with gdb

I have a program which produces a fatal error with a testcase, and I can locate the problem by reading the log and the stack trace of the fatal - it turns out that there is a read operation upon a null pointer.
But when I try to attach gdb to it and set a breakpoint around the suspicious code, the null pointer just cannot be observed! The program works smoothly without any error.
This is a single-process, single-thread program, I didn't experience this kind of thing before. Can anyone give me some comments? Thanks.
Appended: I also tried to call pause() syscall before the fatal-trigger code, and expected to make the program sleep before fatal point and then attach the gdb on it on-the-fly, sadly, no fatal occurred.
It's only guesswork without looking at the code, but debuggers sometimes do this:
They initialize certain stuff for you
The timing of the operations is changed
I don't have a quote on GDB, but I do have one on valgrind (granted the two do wildly different things..)
My program crashes normally, but doesn't under Valgrind, or vice versa. What's happening?
When a program runs under Valgrind,
its environment is slightly different
to when it runs natively. For example,
the memory layout is different, and
the way that threads are scheduled is
different.
Same would go for GDB.
Most of the time this doesn't make any
difference, but it can, particularly
if your program is buggy.
So the true problem is likely in your program.
There can be several things happening.. The timing of the application can be changed, so if it's a multi threaded application it is possible that you for example first set the ready flag and then copy the data into the buffer, without debugger attached the other thread might access the buffer before the buffer is filled or some pointer is set.
It's could also be possible that some application has anti-debug functionality. Maybe the piece of code is never touched when running inside a debugger.
One way to analyze it is with a core dump. Which you can create by ulimit -c unlimited then start the application and when the core is dumped you could load it into gdb with gdb ./application ./core You can find a useful write-up here: http://www.ffnn.nl/pages/articles/linux/gdb-gnu-debugger-intro.php
If it is an invalid read on a pointer, then unpredictable behaviour is possible. Since you already know what is causing the fault, you should get rid of it asap. In general, expect the unexpected when dealing with faulty pointer operations.

segfault only when NOT using debugger

I have a multithreaded C program, which consistently generates a segmentation fault at a specific point in the program. When I run it with gdb, no fault is shown. Can you think of any reason why the fault might occur only when not using the debugger? It's pretty annoying not being able to use it to find the problem!
Classic Heisenbug. From Wikipedia:
Time can also be a factor in heisenbugs. Executing a program under control of a debugger can change the execution timing of the program as compared to normal execution. Time-sensitive bugs such as race conditions may not reproduce when the program is slowed down by single-stepping source lines in the debugger. This is particularly true when the behavior involves interaction with an entity not under the control of a debugger, such as when debugging network packet processing between two machines and only one is under debugger control.
The debugger may be changing timing, and hiding a race condition.
On Linux, GDB also disables address space randomization, and your crash may be specific to address space layout. Try (gdb) set disable-randomization off.
Finally, ulimit -c unlimited and post-mortem debugging (already suggested by Robie) may work.
Perhaps when using gdb memory is mapped in a location which your over/under flow doesn't trample on memory that causes a crash. Or it could be a race condition that is no longer getting tripped. Although it sounds unintuitive, you should be happy your program was nice enough to crash on you.
Some suggestions
Try a static code analyzer such as the free
cppcheck
Try a malloc() debugger like
libefence
Try running it through valgrind
By debugging it you are changing the environment that it is running in. It sounds like you are dealing with some sort of race condition, and by debugging it things are scheduled slightly differently so you don't encounter the issue. That, or things are being stored in a slightly different way so it doesn't occur. Are you able to put some debugging output in the code to assist in figuring out the problem? That may have less of an impact and allow you to find your issue.
I have totally had this problem before! It was a race condition, and when I was stepping though the code with a debugger the thread i was in was slow enough to not trigger the race condition. Pretty awful.
If you're using gcc, try using the -Wall option to get all warnings. If you use an IDE like Eclipse, it would do that automatically.

Debugging unit test in C using check

I'm trying to use check unit testing framework for my C application. But I can't use the debugger (gdb) with it because of two points:
first, check use some complex macros (START_TEST and END_TEST) and the debugger has trouble to put a breakpoint in my code between these two macros (in fact, I can put a software breakpoint but It is never seen by gdb)
second, check define some sort of exceptions by redefining behavior of interruption. Hence, when I try to put a hardware breakpoint, the test failed and exit because check consider the hardware breakpoint as a failure of my test.
Does anyone has already met this problem and has a solution?
Look at the no-fork mode:
Check normally forks to create a separate address space. This allows a signal or early exit to be caught and reported, rather than taking down the entire test program, and is normally very useful. However, when you are trying to debug why the segmentation fault or other program error occurred, forking makes it difficult to use debugging tools.
Actually, you CAN use fork-mode too.
gdb has two interesting options related to fork behaviour:
- detach-on-fork (set this to false)
- follow-on-fork (either parent or child; I always take child)
This will make gdb follow the child process.
When the child process has ended, you have to manually switch back to the parent process by using the inferior command.
I read this and he suggests a very simple solution:
gdb > set environment CK_FORK=no
that worked for me. I could then set a breakpoint in functions the test cases call (that is, the functions under test), and it broke at the right place.
Try TAP (Test Anything Protocol) … it's a lot easier to implement, ship and debug. It's also very easy to make it valgrind-aware and tends to play nicer with gdb.

Resources