Debugging with GDB: Getting Started (2024)

Quick Links

  • What Is GDB?

  • What Is a Core Dump?

  • Installing GDB

  • Core Dump Locations and Issue Reproducibility

  • Reading the Core Dump with GDB

  • Initial Analysis of the Core Dump

  • Backtrace Time!

  • Wrapping up

That application crash need not be the end of the journey! Learn the basics of using GDB, the powerful GNU Debugger and know how to debug core dumps on Linux. Ideal for end users and debugging newcomers alike.

What Is GDB?

The GDB tool is an old-timer, highly respected, debugging utility in the Linux GNU Toolset. It provides it's own command line, a broad array of commands and functions, and step-by-step program (computer code) execution and even modification functionality.

Development on GDB started somewhere in 1986-1988, and in 1988 the tool became part of the Free Software Foundation. It is completely free and can be installed easily on all major Linux distributions.

If you into Windows, then you may like to read Windows Memory Dumps: What Exactly Are They For? instead!

For Linux users, it is important to understand where GDB fits into the process flow when considering computer bugs and errors. There are three possible scenarios. Firstly, there can be an end user running into an application crash who would like to learn a little more about what happened, and figure out if the bug is already known by the community, etc.

This is a common situation, and most advanced users at one point or another will find themselves debugging an application crash. Knowing GDB helps tremendously with this task. More on this below.

The second scenario is a professional (for example an IT consultant or test engineer) running into a crash with an application they also support or maintain. In such cases, the engineer will likely want to debug the crash seen, especially if they are a test engineer, to for example get a backtrace (an overview) of what functions were running at the time of the crash etc. This may help to define the bug better and may help to narrow down a test case.

The third scenario is that of a developer, who will want to use GDB at a more professional level to for example set breakpoints, debug variable watches, do core dump analysis etc. While the scope of this article is a little light for such professionals, there will be a more in-depth GDB article following later. And, if you are a developer and have never worked with GDB, keep reading.

What Is a Core Dump?

If you ever watched Star Trek and heard Captain Picard (or Janeway!) give instruction to "dump the warp core" you will have a fairly good picture of what a core dump may look like. It was basically the core component (the warp core) being ejected as a result of some perceived failure or issue. It was likely a pun take on Linux core dumping.

All fun aside, a core dump is simply a file generated with the (full or partial) state (and memory) of an application, at the time that it crashed.

A core dump is a binary file, which can only be read by a debugger. GDB is such a debugger, and one of the best. The core dump can be written by the crashing application itself (not commonly employed, though it is possible and some larger sever-scale software may use this), but is more often written by the operating system itself.

Some operating systems have core dumping disabled by default, or they minimize the core dumps to a mini dump, which may help with debugging the application, but will likely be much more limited then a full core dump. Then again, a full core dump can be problematic; for example, if you have a system with 128GB of memory, and your application is using most of that memory, a core dump (the file on disk) may be of approximately equal size.

Configuring core dumps on your particular OS is outside of the scope of this article, but this information is relatively easy to find online. Simply search your favorite search engine for a phrase like 'Configure core dumps on Linux Mint', replacing 'Mint' with your Linux operating system name.

Depending on your operating system and it's current setup, it may take a little tweaking and editing in configuration files, a few reboots and at times some light resolving of issues, but once set, you'll be able to use GDB against the core dumps written whenever an application crashes.

Please note that enabling the writing of core dumps on your operating system will as good always be limited to changing configuration settings in existing (or new) configuration files. No other applications need to be installed for core dumps to be enabled and written whenever an application crashes. The tool GDB however, needs to be installed, but will be available in your major Linux distribution's application repository by default.

Installing GDB

To install GDB on your Debian/Apt based Linux distribution (Like Ubuntu and Mint), execute the following command in your terminal:

sudo apt install gdb 

To install GDB on your RedHat/Yum based Linux distribution (Like RHEL, Centos and Fedora), execute the following command in your terminal:

sudo yum install gdb 

Core Dump Locations and Issue Reproducibility

Once you have core dumps enabled, and GDB installed, it is time to find and read your core dump (the file generated by the operating system when your application crashes) with GDB.

If you configured your system for core dumps after your application crashed, it is somewhat likely that no core dump is available for the previous crash. Try and take the same steps in your application to reproduce the crash/issues.

Once that happened, check the default location for core dumps on your Linux distribution (like

/var/crash 

or

/var/lib/systemd/coredump/ 

on Ubuntu/Mint and Centos or

/var/spool/abrt 

on RedHat). Regularly, and at times depending on the settings made, a core dump may have also been written to the directory where the binary (the application) resides (likely), or to it's main working directory (somewhat less likely).

From the ambiguity of the verbiage, you may get the impression that chasing core dumps may be somewhat elusive. That would be an accurate assessment. Whereas the GDB tool itself is very stable, resilient and mature, writing core dumps is a much more haphazard endeavor. There are several reasons for this, the main one being that most major Linux distributions have different implementations of core dumping behavior, and a variety of configuration settings to match.

The writing of core dumps also touches, rather significantly, on security; after all the computer's main memory, in full or in part, are written to the dump, enabling GDB users to read back potentially confidential information. Furthermore, as explained earlier, at times core dump writing can be limited by system resources.

Finally, we are dealing with a crashing application in an unknown state, and it is not always possible to write such a state to disk. It is fair to say that one may expect to spend some time to get core dumping to work reliably and persistently on a given system. This then brings us to the topic of issue reproducibility.

If you have an issue which is consistently reproducible, like your computer always crashing when you play a music file, then configuring core dumps and debugging the same using GDB makes a lot of sense. I once discovered a bug in an Audio driver this way. If you are a test engineer and are repetitively testing a given program, then it makes sense to configure core dumps and use GDB all the more.

However, if you have a single instance of an application crashing, and the issue is not readily reproducible, then you have a choice to make. If you want to be prepared for the next crash of the application, and/or if the application is very important to you (for example for business continuity) then you will want to at least configure core dumps so next time the application crashes, a core dump is generated. GDB can be installed even after a core dump was generated.

Reading the Core Dump with GDB

Now that you have a core dump available for a given crashing application, and have installed GDB, you can easily invoke GDB:

Debugging with GDB: Getting Started (1)
gdb ./myapp ./core 

Here we have an application called myapp which crashes as soon as it is started. On this Ubuntu system, core dumps were enabled by simply setting ulimit -c to a higher number as root and then starting the application. The result is a core dump generated as ./core.

We invoke gdb with two options. The first option is the application/binary/executable which generated a crash. The second is the core dump generated by the operating system as a result of the application crashing.

Initial Analysis of the Core Dump

When GDB starts, as can be seen in the GDB output in the image above, it provides a plethora of information. Carefully reviewing this can provide us with a lot of information about the issue which your system experienced resulting in the application crash.

For example, we immediately note that the program terminated due to SIGFPE error, an arithmetic exception. We also confirm that GDB has correctly identified the executable by the Core was generated by `./myapp' line.

We also see the interesting line/notion No debugging symbols found in ./myapp indicating that debug symbols could not be read from the application binary. Debug symbols is where things can get murky quickly. Most optimized/release level binaries (which would be most of the applications you are running day-to-day) will have the debug symbol information stripped from the resulting binary to save space and increase application runtimes/working efficiency.

Depending on how much was stripped from the resulting optimized binary, even simple function names may not be available. In our case, the function names are still visible, as can be observed from the do_the_maths() function name reference. However, no variables are visible and this is what GDB was referring to with the No debugging symbols found in ./myapp note. When function names are not available, function name references will render as ?? instead of the function name.

We can see however what the crashing frame/function name is: #0 do_the_maths(). You may be wondering what a frame is. The best way to describe and think about a frame is to think about functions, for example do_the_maths(), in a computer program. A single frame is a single function.

Thus, if a program steps through various functions, for example the main() function in a C or C++ program which in turn may call a function called math_function() which finally calls do_the_maths() would lead to three frames, with frame #0 (the final resulting and crashing frame) being the do_the_maths() function, frame #1 being the math_function() and frame #2 (the first called frame, with the highest number) being the main() function.

It is not uncommon to see a 10-20 frames stack in some computer programs, and an occasional 40 or 50 frame stack is quite possible in for example database software. Note that the order the order of the frames is in reverse; crashing frame with frame number #0 first, then going up from there back to the first frame. This makes sense when you think from the debugger/core dump perspective; it started at the point where it crashed, then worked it's way back up to the frames all the way to the main() function.

The term frame stack should now be more self-explanatory; a stack of frames, from most-specific (i.e. do_the_maths) to least-specific (i.e. main()) which will guide us in evaluating what happened. So, can we see this stack/frame stack in GDB for our current issue? We sure can.

Backtrace Time!

Once we have arrived at the gdb prompt, we can issue a backtrace bt command. This command will - for the current thread only (which is most often the crashing thread; GDB auto-detects the crashing threads and auto-places us in that thread, though it does not always get it correct) - dump a backtrace of the frame stack, which we discussed above. In other words, we'll be able to see the flow-through-the-functions the program went through up to the moment it crashed.

Debugging with GDB: Getting Started (2)

Here we executed the bt command, which gives us a nice call stack, or frame stack, or stack, or backtrace - whatever word you prefer, they all mean exactly the same thing. We can see that main() called math_function which in turn called the do_the_maths() function.

As we can see, variable names are not displayed in this particular output, and GDB notified us that debug symbols were not available. Though the resulting binary still had some level of debugging information available, as all frame/function names showed correctly and no frames rendered as ??.

I thus recompiled the application from source using the -ggdb option to gcc (as in gcc -ggdb ...) and note how there is much more information available:

Debugging with GDB: Getting Started (3)

This time we can clearly see that GDB is reading symbols (as indicated by Reading symbols from ./myapp...), and that we can see variable names like x and y. Provided that the source code is available, we can even see the exact source code line where the issue happened (as indicated by 3 o=x/y;). We can also see the source code lines for all frames.

Studying the output a little, we immediately realize what is wrong. The variable o was being set to the variable x being divided by y. However, looking a little closer at the function's input (shown by (x=1, y=0)), we see that the application tried to divide a number by zero, which is a mathematical impossibility, leading to the SIGFPE (Arithmetic exception) signal.

Our issue is thus no different from typing 1 divided by 0 on your calculator; it too will complain, though not crash ;)

In such a case, a workaround may be devised by providing different input to the application (where you are trying to work around a certain crash and do not have the source code available) - for example you could try and input 0.000000001 instead of 0 and accept a small rounding adjustment, or - provided the source is available and you are improving source code as a developer - you could add some additional code in your program to cater for mistaken inputs to the y variable.

As you can see, GDB is a very versatile tool, in the variety of roles and situations one may find themselves in. Furthermore, we have only just scratched the surface of what the cool can do. It is also exactly depending on the situation and the surrounding information (is a core dump available, do we have debug symbols, etc.) how far one can take the GDB journey in each instance. But one thing is clear; one is much more likely to debug a given situation more fully if, when and how core dumps and GDB are used.

Wrapping up

In this article, we introduced GDB, the GNU debugger which can be easily installed and used on any major Linux distribution. We discussed the need to configure core dumps on the target system first, and the intricacies thereof. We saw how to install GDB, allowing us to read and process the generated core dumps.

We next reviewed basic interfacing between the core dump and the user or developer, and provided a practical example of an actual analysis, looking at the bt backtrace command. We also discussed optimized versus debug [symbol] instrumented application builds and how this affects the level of information visibility inside GDB.

In a future article, we'll dive deeper into GDB and explore more advanced GDB use.

Enjoy Debugging!

Debugging with GDB: Getting Started (2024)

FAQs

How to do debugging using GDB? ›

Debugging a program that produces a core dump
  1. Compile the program using the following command. g++ testit.c �g �o testit.
  2. Run it normally, you should get the following result: ...
  3. The core dump generates a file called corewhich can be used for debugging. ...
  4. As we can see from the output above, the core dump was produced.

How do you start a program using GDB? ›

Starting your program. Use the run command to start your program under GDB. You must first specify the program name (except on VxWorks) with an argument to GDB (see section Getting In and Out of GDB), or by using the file or exec-file command (see section Commands to specify files).

How do you go step by step in GDB? ›

To execute one line of code, type "step" or "s". If the line to be executed is a function call, gdb will step into that function and start executing its code one line at a time. If you want to execute the entire function with one keypress, type "next" or "n".

How to debug C program using GDB in 6 simple steps? ›

How to Debug C Program using gdb in 6 Simple Steps
  1. Write a sample C program with errors for debugging purpose. ...
  2. Compile the C program with debugging option -g. ...
  3. Launch gdb. ...
  4. Set up a break point inside C program. ...
  5. Execute the C program in gdb debugger. ...
  6. Printing the variable values inside gdb debugger.
Sep 28, 2018

How to use GDB for kernel debugging? ›

To configure the debugging infrastructure with GDB, there are three steps:
  1. Compile the kernel with KGDB support.
  2. Start the kernel debugger (KGDB) in the target.
  3. Connect to the kernel debugger using a GDB client in the host.
Jan 15, 2024

How do you practice code debugging? ›

Here are some of the best practices for debugging and troubleshooting code.
  1. Reproduce the problem. The first step in debugging code is to reproduce the problem. ...
  2. Use the browser's developer tools. ...
  3. Use logging and debugging statements. ...
  4. Use breakpoints. ...
  5. Isolate the problem. ...
  6. Use version control. ...
  7. Test early and often. ...
  8. Use a linter.
Feb 16, 2023

How do I start a process in GDB server? ›

To start gdbserver without supplying an initial command to run or process ID to attach, use the --multi command line option. Then you can connect using target extended-remote and start the program you want to debug. In multi-process mode gdbserver does not automatically exit unless you use the option --once .

How to use GDB with source code? ›

When you start GDB, its source path includes only ' $cdir ' and ' $cwd ', in that order. To add other directories, use the directory command. The search path is used to find both program source files and GDB script files (read using the ' -command ' option and ' source ' command).

How to set breakpoints in GDB? ›

Breakpoints are set with the break command (abbreviated b ). The debugger convenience variable `$bpnum' records the number of the breakpoint you've set most recently; see section Convenience variables, for a discussion of what you can do with convenience variables.

How to debug C code? ›

Here's a general outline of the process:
  1. Compile your program with debugging symbols: Add the -g flag to your compiler command to include debugging information in the executable file. ...
  2. Launch the debugger: Run your program under a debugger.

How to debug code in Linux? ›

To use GDB in Linux, you first compile your code with the -g flag, then run it with gdb . This allows you to debug your program and uncover any hidden bugs. In this example, we first compile the myprogram. c file with the -g flag using the gcc command.

What are the 7 debug steps? ›

Process of Debugging
  • Step 1: Reproduce the Bug. To start, you need to recreate the conditions that caused the bug. ...
  • Step 2: Locate the Bug. Next, find where the bug is in your code. ...
  • Step 3: Identify the Root Cause. Now, figure out why the bug happened. ...
  • Step 4: Fix the Bug. ...
  • Step 5: Test the Fix. ...
  • Step 6: Document the Process.
Jun 13, 2024

How do I start a program in GDB? ›

4.2 Starting your Program. Use the run command to start your program under GDB. You must first specify the program name with an argument to GDB (see Getting In and Out of GDB), or by using the file or exec-file command (see Commands to Specify Files).

How do I run a Python script in GDB? ›

It is also possible to execute a Python script from the GDB interpreter: source script-name. The script name must end with ' . py ' and GDB must be configured to recognize the script language based on filename extension using the script-extension setting.

How do I restart a program in GDB? ›

1.3 How do I restart a program running in the debugger? [top] [toc] Use the kill command in gdb to stop execution. The you can use the run command as shown above to start it again. (gdb) kill Kill the program being debugged? (y or n) y (gdb) run ...

Top Articles
Latest Posts
Recommended Articles
Article information

Author: Mr. See Jast

Last Updated:

Views: 5535

Rating: 4.4 / 5 (75 voted)

Reviews: 90% of readers found this page helpful

Author information

Name: Mr. See Jast

Birthday: 1999-07-30

Address: 8409 Megan Mountain, New Mathew, MT 44997-8193

Phone: +5023589614038

Job: Chief Executive

Hobby: Leather crafting, Flag Football, Candle making, Flying, Poi, Gunsmithing, Swimming

Introduction: My name is Mr. See Jast, I am a open, jolly, gorgeous, courageous, inexpensive, friendly, homely person who loves writing and wants to share my knowledge and understanding with you.