Overcome your fear: Use GDB – By Moria Zuberi

Overcome your fear: Use GDB – By Moria Zuberi
03 Apr 2019

So, you’re a talented programmer, and you’ve written some code that didn’t get even a single compilation or linker error. You ran it and got “segmentation fault”. How is that possible? I mean, it was soooo perfect! Right?

Wrong! Whenever you write code, you also write bugs. Noticing that something isn’t working (like getting a segmentation fault) is one thing, but finding what exactly went wrong – is something else.

If you are an Embedded-Linux programer, you probably know that there are several methods for debugging. The main two approaches are prints and debuggers. Each method has its benefits and limitations.

GDB ( GNU Debugger) is a command line debugger tool. It is not new and not shiny, but it definitely gets the job done, and it’s very easy to use. I guarantee that if you dedicate the next 10 minutes to reading this article you will have a new tool in your toolbox.

Let’s take a closer look.

Why use GDB?

GDB is a tool that you can use on various platforms, without depending on the presence of an IDE. In fact, some IDE debuggers are even based on GDB. So, it’s not a bad thing working with the source and expanding your debug abilities.

GDB is quick and easy to use with some basic functions and it can save a lot of repetitive work (as can happen with prints). It works with C,C++ and other languages as well. Support packages even make it possible to work with Python.

GDB can be used in 2 ways:

    • As a debug tool during runtime (can attach to already running process)
    • To analyze a core dump (file that contains a snapshot of the program’s memory during its crash)

So, get over the GDB appearance, you’re a Linux programmer!

GDB basics

In order to work with GDB, you first need to compile your code with the -g flag, in order for it to have information for GDB.
g++ main.cpp -g -Wall -o myProg

Run your program with GDB:
gdb <executable name>Once inside the GDB prompt, you may use the following commands (and many others – please refer to online manuals):

help <command> - to get some helpful info on any command
run / r - to start running your program
break / b <source code line number> - to add a breakpoint. May also be used with a condition.
continue / c - continue running after breakpoint or ctrl + c.
step / s - go through the code line by line and enter the next function.
next / n - go through the code line by line without entering functions.
print / p <variable> - print variable value. May also be used for complex data structs.
set <variable> = <value> - change variable value.

For a lot more useful info and examples, visit the following links:

gdb basics – manual #1

gdb basics – manual #2

Of course, you can find more examples and info online.

Core Dump

Quoting Wikipedia: “Core dump consists of the recorded state of the working memory of a computer program at a specific time, generally when the program has crashed or otherwise terminated abnormally”.

Remember that segmentation fault we talked about at the beginning of the article? That is a prime example of when to create a core dump file for analysis.

Creating a core dump file depends on what shell you are using. If you are using Bash, use the following command and run your program (to let your program dump memory info during crash):
ulimit -c unlimited
Now that you have a dump file – load it into GDB:
gdb <executable name> <dump file name>
What we see now, is some GDB information (copyrights, etc.) and then our crash info.
For example:
Core was generated by './myProg'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  __memcpy_avx_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-avx-unaligned.S:138
138../sysdeps/x86_64/multiarch/memcpy-avx-unaligned.S: No such file or directory.

This means that the core was generated by “myProg” and the function that crashed the program is  __memcpy_avx_unaligned().

Now we can use the following commands:

backtrace / bt - to see the function calls in the stack that got us up to this crash.
up - to get information about the previous function in the stack
down - to get information about the next function in the stack
list - to view function or line in the code

In our example:

(gdb) bt
#0  __memcpy_avx_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-avx-unaligned.S:138
#1  0x000000000040078e in main () at main.c:35

The bt command show us that the crash happened in line 35 of main.c.

(gdb) up
#1  0x000000000040078e in main () at main.c:35
35 memcpy(dest, src, strlen(src)+1);

The up command goes back to the previous frame in the stack and shows the specific line in main.c.

(gdb) list
30 unsigned int page, reg;
31 unsigned int gray;
32 char src[] = "hello world";
33 char* dest;
35 memcpy(dest, src, strlen(src)+1);
37 printf("please select your test: 1 - paging, 2 - gray\n");
38 scanf("%ld", &val);
39 if(1 == val)

Finally, list gives us more context and we can definitely see that dest is used without memory allocation.

For more info and examples, visit the following link:


Wrapping Up

GDB is a powerful and easy tool, it doesn’t require much effort to set up and you can use it quickly with just a few basic functions. It offers some very useful options, such as conditional breakpoints, close-watch variables, core-dump analysis, and even interactive change of variable value to check your possible solution.

That’s it, I hope you got to know GDB a little and won’t hesitate to use it the next time you get a segmentation fault. 🙂