160

This is intended to be a general-purpose question to assist new programmers who have a problem with a program, but who do not know how to use a debugger to diagnose the cause of the problem.

This question covers three classes of more specific question:

  • When I run my program, it does not produce the output I expect for the input I gave it.
  • When I run my program, it crashes and gives me a stack trace. I have examined the stack trace, but I still do not know the cause of the problem because the stack trace does not provide me with enough information.
  • When I run my program, it crashes because of a segmentation fault (SEGV).
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • 6
    Nice work - it would also be good to have a related "go to" Q & A for *debugging techniques*, e.g. using a debugger, other debug tools (e.g. valgrind), strategic printfs, stress testing, divide and conquer, etc. – Paul R Aug 19 '14 at 13:52
  • 1
    I agree with @PaulR, the FAQ should contain stuff like this. – Nicu Stiurca Oct 29 '14 at 18:33
  • 1
    This question is tagged as "language-agnostic", yet it contains a link to a question that is specific to the Java programming language. I'm afraid that this link may cause more confusion than help, because most people reading the question probably do not know Java. – Andreas Wenzel May 09 '21 at 19:21
  • 1
    @AndreasWenzel While the linked question is about Java, the general concept of a stack trace is very widely-used concept across many languages. C, C++, C#/.NET, JavaScript, Python, Rust, and more all have the concept of a stack and stack tracing. – TylerH Feb 18 '22 at 14:31
  • @TylerH: My concern was not about using the term "stack trace", but rather about linking that text to a language-specific question. For example, it may be better to instead link to [the Wikipedia article on "stack trace"](https://en.wikipedia.org/wiki/Stack_trace) instead, because that article is not specific to the Java programming language, but also contains references to other programming languages. – Andreas Wenzel Mar 08 '22 at 11:12
  • 1
    In 2022 there are just 2 answers to this question and none of them mention the stack or the segmentation faults. Maybe it's time to edit the question – Christian Vincenzo Traina Sep 06 '22 at 12:23
  • 2
    Those who want to use this as a general reference in comments or as a duplicate target ought to read the *unspecific* answers first (e.g., there is very little ***'how'*** in them). There are currently [1,683 linked questions](https://stackoverflow.com/questions/linked/25385173?sort=newest) to this one. [Related meta question](https://meta.stackoverflow.com/questions/421505/when-should-one-duplicate-a-question-to-what-is-a-debugger-and-how-can-it-help). – Peter Mortensen Nov 18 '22 at 15:38

3 Answers3

102

A debugger is a program that can examine the state of your program while your program is running. The technical means it uses for doing this are not necessary for understanding the basics of using a debugger. You can use a debugger to halt the execution of your program when it reaches a particular place in your code, and then examine the values of the variables in the program. You can use a debugger to run your program very slowly, one line of code at a time (called single stepping), while you examine the values of its variables.

Using a debugger is an expected basic skill

A debugger is a very powerful tool for helping diagnose problems with programs. And debuggers are available for all practical programming languages. Therefore, being able to use a debugger is considered a basic skill of any professional or enthusiast programmer. And using a debugger yourself is considered basic work you should do yourself before asking others for help. As this site is for professional and enthusiast programmers, and not a help desk or mentoring site, if you have a question about a problem with a specific program, but have not used a debugger, your question is very likely to be closed and downvoted. If you persist with questions like that, you will eventually be blocked from posting more.

How a debugger can help you

By using a debugger you can discover whether a variable has the wrong value, and where in your program its value changed to the wrong value.

Using single stepping you can also discover whether the control flow is as you expect. For example, whether an if branch executed when you expect it ought to be.

General notes on using a debugger

The specifics of using a debugger depend on the debugger and, to a lesser degree, the programming language you are using.

  • You can attach a debugger to a process already running your program. You might do it if your program is stuck.

  • In practice it is often easier to run your program under the control of a debugger from the very start.

  • You indicate where your program should stop executing by indicating the source code file and line number of the line at which execution should stop, or by indicating the name of the method/function at which the program should stop (if you want to stop as soon as execution enters the method). The technical means that the debugger uses to cause your program to stop is called a breakpoint and this process is called setting a breakpoint.

  • Most modern debuggers are part of an IDE and provide you with a convenient GUI for examining the source code and variables of your program, with a point-and-click interface for setting breakpoints, running your program, and single stepping it.

  • Using a debugger can be very difficult unless your program executable or bytecode files include debugging symbol information and cross-references to your source code. You might have to compile (or recompile) your program slightly differently to ensure that information is present. If the compiler performs extensive optimizations, those cross-references can become confusing. You might therefore have to recompile your program with optimizations turned off.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Raedwald
  • 46,613
  • 43
  • 151
  • 237
  • 7
    This is incomplete as it misses the most important debugger of all, one that has the potential to reduce the number of questions on Stackoverflow very significantly (I predict by at least 20%) - javascript debuggers: firebug, Chrome, Firefox, IE9+ integrated debugger, IE8- Visual Studio, etc. – slebetman Aug 25 '14 at 02:45
  • 3
    Also for node.js - node inspector. But node.js programmers don't ask as many basic and/or fix-my-code questions as general javascript programmers. – slebetman Aug 25 '14 at 02:47
  • 2
    It might be useful to add use cases for the basic debugging ideas like setting breakpoints, watching variables and different types of steps, as well as detailing the general process you'd follow to actually investigate an issue with a debugger. At the moment this seems to be more "you should learn to use a debugger" and less "here's how you use a debugger". – Bernhard Barker Mar 29 '21 at 13:07
  • A link to [rr](https://rr-project.org/) seems relevant here. Sometimes it is *very* useful to be able to travel back in time when debugging as well as being able to replay the exact same run of a program multiple times. – Jesper Juhl Apr 12 '23 at 11:05
66

I want to add that a debugger isn't always the perfect solution, and shouldn't always be the go-to solution to debugging. Here are a few cases where a debugger might not work for you:

  • The part of your program which fails is really large (poor modularization, perhaps?) and you're not exactly sure where to start stepping through the code. Stepping through all of it might be too time-consuming.
  • Your program uses a lot of callbacks and other non-linear flow control methods, which makes the debugger confused when you step through it.
  • Your program is multi-threaded. Or even worse, your problem is caused by a race condition.
  • The code that has the bug in it runs many times before it bugs out. This can be particularly problematic in main loops, or worse yet, in physics engines, where the problem could be numerical. Even setting a breakpoint, in this case, would simply have you hitting it many times, with the bug not appearing.
  • Your program must run in real-time. This is a big issue for programs that connect to the network. If you set up a breakpoint in your network code, the other end isn't going to wait for you to step through, it's simply going to time out. Programs that rely on the system clock, e.g. games with frameskip, aren't much better off either.
  • Your program performs some form of destructive actions, like writing to files or sending e-mails, and you'd like to limit the number of times you need to run through it.
  • You can tell that your bug is caused by incorrect values arriving at function X, but you don't know where these values come from. Having to run through the program, again and again, setting breakpoints farther and farther back, can be a huge hassle. Especially if function X is called from many places throughout the program.

In all of these cases, either having your program stop abruptly could cause the end results to differ, or stepping through manually in search of the one line where the bug is caused is too much of a hassle. This can equally happen whether your bug is incorrect behavior, or a crash. For instance, if memory corruption causes a crash, by the time the crash happens, it's too far from where the memory corruption first occurred, and no useful information is left.

So, what are the alternatives?

Simplest is simply logging and assertions. Add logs to your program at various points, and compare what you get with what you're expecting. For instance, see if the function where you think there's a bug is even called in the first place. See if the variables at the start of a method are what you think they are. Unlike breakpoints, it's okay for there to be many log lines in which nothing special happens. You can simply search through the log afterward. Once you hit a log line that's different from what you're expecting, add more in the same area. Narrow it down farther and farther, until it's small enough to be able to log every line in the bugged area.

Assertions can be used to trap incorrect values as they occur, rather than once they have an effect visible to the end-user. The quicker you catch an incorrect value, the closer you are to the line that produced it.

Refactor and unit test. If your program is too big, it might be worthwhile to test it one class or one function at a time. Give it inputs, and look at the outputs, and see which are not as you're expecting. Being able to narrow down a bug from an entire program to a single function can make a huge difference in debugging time.

In case of memory leaks or memory stomping, use appropriate tools that are able to analyze and detect these at runtime. Being able to detect where the actual corruption occurs is the first step. After this, you can use logs to work your way back to where incorrect values were introduced.

Remember that debugging is a process going backward. You have the end result - a bug - and find the cause, which preceded it. It's about working your way backward and, unfortunately, debuggers only step forwards. This is where good logging and postmortem analysis can give you much better results.

SlugFiller
  • 1,596
  • 12
  • 12
  • 13
    This would be a good answer... of a different question. It is a bad answer for *this* question. Perhaps you should ask that question and post this as a reply to it. – Raedwald Apr 07 '15 at 06:53
  • 25
    The actual question is described as "assist new programmers who have a problem with a program", "it does not produce the output I expect" and "I have examined the stack trace, but I still do not know the cause of the problem". All of which are aided by this answer. Additionally, when explaining what a debugger does, it's equally important to explain what it *doesn't* do. – SlugFiller Apr 07 '15 at 14:51
  • 5
    Great answer. I always used the debugger as main tool to find bugs. But now i'm working in a project where a huge infrastructure component is using many threads and a lot of network code(client/server) and notice that the debugger is the last thing that helps me. You have mentioned a lot of things where you should really use a different tool instead of relying on your good old debugger (most important: logging). – Tim Schmelter Nov 24 '17 at 10:02
  • *"You can tell that your bug is caused by incorrect values arriving at function X but you don't know where these values come from"* This is especially hard to debug. How do you usually go about fixing something like that? – Aykhan Hagverdili Jan 08 '19 at 16:33
  • 3
    @Ayxan To some extent, if you've managed to make the function break on an assert, you can use the call stack to get the caller. But that alone doesn't give you the source of the value, because the value is most likely from an earlier line. You basically have to follow the value back, through the various variables it goes through. If you have a good idea of the path the data takes, you can just create a bunch of log prints, and try to narrow down where it "goes wrong". If not, you'll basically need a separate run of the program (reproducing the error) for every step back. – SlugFiller Jan 10 '19 at 23:09
  • Both answers are great. Just in addition, we are most likely not able to debug on production, so the logging comes very handy, especially when the logging is done right (you could improve the answer by adding info or link with some best practices on how to write good, not extensive logging) – itwasntme Jan 19 '20 at 18:02
  • 4
    `The code that has the bug in it runs many times before it bugs out` this is easy to solve. All good debuggers have the capability to do conditional breakpoints. For example in MSVC you can [set it to break after the breakpoint is hit a number of times, or a specific condition](https://learn.microsoft.com/en-us/visualstudio/debugger/using-breakpoints?view=vs-2019#breakpoint-conditions) like `str == "abc" && i > 40000` – phuclv Jul 16 '21 at 16:44
  • 1
    Similarly in gdb you use [`break 10 if i >= 10000000`](https://sourceware.org/gdb/current/onlinedocs/gdb/Conditions.html) and in clang something like [`breakpoint set --name foo --condition '(int)strcmp(y,"hello") == 0'`](https://lldb.llvm.org/use/map.html#id2) – phuclv Jul 16 '21 at 16:46
  • @phuclv This would imply you already know the exact number of cycles before a program hits an invalid state, or the exact variable that goes bad first. Given that this is generally the very thing you're trying to figure out with debugging, this is begging the question. Even if you know a certain variable goes bad, if it goes bad due to a previous variable going bad, you need to adjust the breakpoint, and run the program again. It's also limited to simple variables within the breakpoint's scope. It can't, for example, do complex verification like checking that a doubly-linked list isn't broken. – SlugFiller Jul 18 '21 at 14:36
0

By Definition:

A debugger is a software application that allows programmers to inspect the state of a program while it is running, enabling them to understand its behavior and track down errors and bugs.

Debugging is an essential part of the software development process because

  • it helps improve code quality.
  • it ensures that programs run as expected.

Let's dive into each of the scenarios mentioned in your question to understand how a debugger can help in each case:

1- Program Output Mismatch:

When you run your program and the output does not match your expectations for a given input, a debugger can help you investigate the root cause. The debugger allows you to pause the program's execution at specific points, known as breakpoints, and inspect the program's state, including variable values, function call stack, and more. By examining the variables and their values, you can identify where the program deviates from the expected behavior. This process is particularly helpful when dealing with complex conditions or loops that may cause unexpected results.

2- Crashes and Stack Traces:

When a program crashes and produces a stack trace, it indicates that an unhandled exception or error has occurred. The stack trace provides information about the sequence of function calls that led to the crash but may not directly reveal the exact cause of the issue. A debugger helps by allowing you to attach to the program when it crashes, examine the stack trace in real-time, and interactively step through the code to find the point where the crashing occurs. By observing the variable values and the program's state at that specific moment, you gain insights into what went wrong and identify potential null pointers, memory corruption, or other lot more issues that led to the crash.

3- Segmentation Fault (SEGV):

A segmentation fault occurs when a program attempts to access a restricted memory region, often due to null pointers or accessing an invalid memory location. Debugging segmentation faults can be particularly challenging without a debugger. A debugger assists in locating the exact line of code that triggered the segmentation fault and provides insights into the memory addresses being accessed. It allows you to inspect the memory at the faulting address and nearby locations, helping you understand what caused the memory violation.

Other powerful features of a debugger:

- Stepping through Code:

As Debuggers allow you to step through your code line-by-line, executing it one step at a time. This feature helps you understand the program's flow and behavior more effectively.

- Variable Inspection:

You can also inspect the values of variables at any point during program execution. This ability allows you to identify when variables are not holding the expected values, helping you track down logic errors.

- Conditional Breakpoints:

You can also set breakpoints that trigger only when specific conditions are met. This functionality is useful when you want to investigate specific situations or iterations in loops.

- Watchpoints:

The Watchpoints let you monitor the value of a variable and stop the program's execution when that value changes. This feature is valuable for tracking changes to critical variables in large codebases.

- Backtrace and Call Stack:

A debugger can display the call stack, showing the sequence of function calls that led to the current point of execution. This information is immensely helpful in understanding how the program reached its current state.

- Memory Inspection:

Debuggers allow you to inspect memory contents, which can be crucial for understanding and fixing issues related to data corruption or pointer manipulation.

Overall, using a debugger is an essential skill for any programmer. It helps you identify and resolve issues efficiently, leading to more robust and reliable software. Familiarizing yourself with the debugger relevant to your programming language and environment is highly recommended.

Common debuggers include:

  • gdb for C/C++.
  • pdb for Python.
  • Visual Studio Debugger for Visual Studio.
  • LLDB for macOS and iOS development.

Invest your time in debugging:

because it will improve your ability to diagnose and fix problems in your code, ultimately making you a more proficient and confident programmer.

Hope it helps.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
Muhammad Ali
  • 956
  • 3
  • 15
  • 20