GDB
Updated 751 Days AgoPublic

GDB is the GNU Debugger and is used for debugging application that are behaving in an unexpected manner, or for tracking down the cause of a segmentation fault.

When to use GDB
GDB can only be used on compiled ELF applications. It cannot be used to debug Python scripts or other interpreted programs. However, if a segmentation fault occurs in a python script for example, then the cause could be traced back to the python ELF executable (or one its loaded libraries), and in that case, GDB can be useful to figure out which part of python or its libraries is causing the crash.

How to run GDB
There are a couple of ways to run GDB.
First, you can attach it to a running process, by using the -p option. For example :

gdb -p `pidof gnome-calculator`

The other possibility for running GDB is to run it on an already generated core file. If an application crashes with a segmentation fault, and a core file is generated by the system, you can run GDB and pass the core file to it, which will allow you to analyze what happened when the segmentation fault occured.
In order to do that, you need to pass the executable that caused the segfault as an argument, and the core file itself as the second argument to gdb. For example :

gdb chrome core.12345

Note: In order for a core dump to be generated, you need to have your system setup for it. Running 'ulimit -c unlimited' is required for example to set the maximum filesize of the core file to 'unlimited' (it is by default set to 0 which disables writing core files).

If you omit the core file in the arguments to gdb, then gdb will run with the application as the target for debugging, such as :

gdb chrome

If you want to pass specific arguments to the application, then you can do it by using the argument '--args' to gdb, such as :

gdb --args ./program argument1 argument2

If you forget to use the --args argument to GDB, then whatever extra argument you specify will be considered as the path to a core dump, and you will likely get the "X is not a valid core dump" warning from GDB.

Running an application inside GDB
Once GDB is running, you will get access to the command prompt of GDB, but the actual process will not be running. If you attached to an existing process using the -p argument, then the process will be running but frozen (interrupted). If you gave GDB a core file, then you will be in the state of termination of the application, where you can analyze data/memory, even though the application is not running.
To start the process after running GDB, you must enter the command :

(gdb) run

This will start the process. If the process is already started (and you interrupted it), then typing the command 'run' will kill the current process and will restart the process from the beginning. It can be useful if you want to restart your application to test again for a bug that happens at startup for example. You will get a warning however :

(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n)

If you don't want gdb to start the process from the beginning, you should use this command instead :

(gdb) continue

which will tell GDB to resume the current process.

Whenever the process is running and executing code, the gdb prompt will be unavailable. If you want to have access to the gdb prompt in order to examine the memory or the execution stack, you can interrupt a running program using Ctrl-C. That will give you access to the gdb command prompt. The process will simply be interrupted and it can be resumed at any time with the command 'continue' as explained above.
You can also have gdb interrupted by setting breakpoints before running the application.
GDB will also automatically be interrupted if a segmentation fault or Abort happens in an application while it was running inside of GDB.

Analyzing a crash
The most useful thing with gdb is to be able to look at the backtrace in order to know exactly what function was being executed when a crash happened. In order to do that, we run the command :

(gdb) backtrace

This will print out the stack trace of the current execution, at the point where the application was interrupted (whether due to a crash, or ctrl-C from the user, or a breakpoint, etc..)
The backtrace looks like this :

#0  cura::GCodeExport::isOpened (this=this@entry=0x7fffffffd468) at src/gcodeExport.cpp:107
#1  0x0000000000418f8f in cura::fffProcessor::processFile (this=this@entry=0x7fffffffd460, files=...) at src/fffProcessor.h:70
#2  0x000000000040df19 in main (argc=2, argv=0x7fffffffdb18) at src/main.cpp:194

Each line will give you the frame number (how far in the stack call trace it is), followed by the return address in the function, the name of the function and its arguments, and the filename+line number of where it is. If the function is from an external library,the name of the library will also be displayed.
You can also use the command :

(gdb) backtrace full

In order to have gdb print the backtrace as well as all of the local variables used in that function, which can be useful to have all of the local variables data at a glance, especially when sending a backtrace to someone else to view, as they will not have access to the interactive debugger.
In order to print the value of a specific variable, you can use the 'print' command :

(gdb) print processor.insetXConfig
$3 = {speed = 0, lineWidth = 0, name = 0x0, spiralize = false}

Note that doing a 'print' such as this will only work if the variable is accessible in the current frame. The 'frame' is used to refer to the current scope in a C application, for example, to find out the current frame, type the command :

 (gdb) frame
#0  cura::GCodeExport::writeRetraction (this=this@entry=0x7fffffffd468, force=force@entry=false) at src/gcodeExport.cpp:313
313	    if (retractionAmount > 0 && !isRetracted && (extrusionAmountAtPreviousRetraction + minimalExtrusionBeforeRetraction < extrusionAmount || force))

In order to change the current frame, you can type the frame number after the 'frame' command, such as :

(gdb) frame
#0  cura::GCodeExport::writeRetraction (this=this@entry=0x7fffffffd468, force=force@entry=false) at src/gcodeExport.cpp:313
313	    if (retractionAmount > 0 && !isRetracted && (extrusionAmountAtPreviousRetraction + minimalExtrusionBeforeRetraction < extrusionAmount || force))
(gdb) print config.extruderOffset 
No symbol "config" in current context.
(gdb) bt
#0  cura::GCodeExport::writeRetraction (this=this@entry=0x7fffffffd468, force=force@entry=false) at src/gcodeExport.cpp:313
#1  0x0000000000405358 in cura::GCodeExport::finalize (this=this@entry=0x7fffffffd468, maxObjectHeight=0, moveSpeed=150, 
    endCode=0x862c58 "M104 S0", ' ' <repeats 21 times>, ";extruder heater off\nM140 S0", ' ' <repeats 21 times>, ";heated bed heater off (if you have it)\nG91", ' ' <repeats 28 times>, ";relative positioning\nG1 E-1 F300", ' ' <repeats 19 times>...) at src/gcodeExport.cpp:416
#2  0x000000000040df4e in finalize (this=0x7fffffffd460) at src/fffProcessor.h:93
#3  main (argc=1, argv=0x7fffffffdb18) at src/main.cpp:200
(gdb) frame 3
#3  main (argc=1, argv=0x7fffffffdb18) at src/main.cpp:200
200	    processor.finalize();
(gdb) print config.extruderOffset 
$5 = {{X = 0, Y = 0} <repeats 16 times>}

This will allow you to explore and analyze the value of variables in functions further back in the trace, which can help in figuring out what caused the crash in the current frame.

Multithreading considerations
If an application is multi-threaded, then we can obtain the backtrace of all threads by using the command 'thread apply all bt', for example :

$ gdb -p `pidof adb`
[...]

(gdb) thread apply all bt

Thread 2 (Thread 0x7fc2e9723700 (LWP 14042)):
#0  0x00007fc2e99f0d3d in nanosleep () from /lib64/libc.so.6
#1  0x00007fc2e99f0bd4 in sleep () from /lib64/libc.so.6
#2  0x000055b1a2d1baa4 in device_poll_thread ()
#3  0x00007fc2ea2ee555 in start_thread () from /lib64/libpthread.so.0
#4  0x00007fc2e9a2aded in clone () from /lib64/libc.so.6

Thread 1 (Thread 0x7fc2ea8fe700 (LWP 14041)):
#0  0x00007fc2e9a212e3 in select () from /lib64/libc.so.6
#1  0x000055b1a2d100f6 in fdevent_process ()
#2  0x000055b1a2d11068 in fdevent_loop ()
#3  0x000055b1a2d05d34 in adb_main ()
#4  0x000055b1a2d0b307 in adb_commandline ()
#5  0x00007fc2e9948700 in __libc_start_main () from /lib64/libc.so.6
#6  0x000055b1a2d03919 in _start ()
(gdb) thread 2
[Switching to thread 2 (Thread 0x7fc2e9723700 (LWP 14042))]
#0  0x00007fc2e99f0d3d in nanosleep () from /lib64/libc.so.6
(gdb) thread 1
[Switching to thread 1 (Thread 0x7fc2ea8fe700 (LWP 14041))]
#0  0x00007fc2e9a212e3 in select () from /lib64/libc.so.6
(gdb) frame 5
#5  0x00007fc2e9948700 in __libc_start_main () from /lib64/libc.so.6

As you can see, we can switch from one thread to another using the 'thread' command, at which point we can use the regular 'frame' and 'bt' or 'print' commands to explore the cause of the issue.

Debugging a running application
In order to track down a specific bug, we can use GDB to do a step-by-step execution of the code. First, we would need to setup a breakpoint on the function that we want to investigate, by running the command :

(gdb) break <breakpoint address>

The breakpoint address can be the name of a function, or a line number or a random assembly code address.

Once we have the GDB prompt, we can run the command :

(gdb) next

in order to execute the next line of code in the application.
You can use the command :

(gdb) list

in order to list 10 lines of code around the current line of code being executed. You can also give arguments to the 'list' command to specify which line number, which file or which function to list the code of.
If a line of code executes some other function, running 'next' will of course skip it in order to go to the next line of code in the current function. If that is not the behavior you want, and you want to enter inside of the function being called, then you can use the command :

(gdb) step

which will step-into the function being called.

Here is an example of how to step to the next line vs. step into a function :

(gdb) break cura::GCodeExport::writeRetraction 
Breakpoint 1 at 0x404e38: file src/gcodeExport.cpp, line 309.
(gdb) break src/gcodeExport.cpp:321
Breakpoint 2 at 0x404f00: file src/gcodeExport.cpp, line 321.
(gdb) run
Starting program: /home/kakaroto/coding/alephobjects/CuraEngine/build/CuraEngine 
Cura_SteamEngine version DEV
Copyright (C) 2014 David Braam

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
Default config 'default.cfg' not used
M107

Breakpoint 1, cura::GCodeExport::writeRetraction (this=this@entry=0x7fffffffd468, force=force@entry=false) at src/gcodeExport.cpp:309
309	{
(gdb) continue
Continuing.
G1 F2700 E-4.50000

Breakpoint 2, cura::GCodeExport::writeRetraction (this=this@entry=0x7fffffffd468, force=force@entry=false) at src/gcodeExport.cpp:321
321	            estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount - retractionAmount), currentSpeed);
(gdb) next
323	        if (retractionZHop > 0)
(gdb) list
318	        }else{
319	            fprintf(f, "G1 F%i %c%0.5f\n", retractionSpeed * 60, extruderCharacter[extruderNr], extrusionAmount - retractionAmount);
320	            currentSpeed = retractionSpeed;
321	            estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount - retractionAmount), currentSpeed);
322	        }
323	        if (retractionZHop > 0)
324	            fprintf(f, "G1 Z%0.3f\n", INT2MM(currentPosition.z + retractionZHop + zOffset));
325	        extrusionAmountAtPreviousRetraction = extrusionAmount;
326	        isRetracted = true;
327	    }
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/kakaroto/coding/alephobjects/CuraEngine/build/CuraEngine 
Cura_SteamEngine version DEV
Copyright (C) 2014 David Braam

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
Default config 'default.cfg' not used
M107

Breakpoint 1, cura::GCodeExport::writeRetraction (this=this@entry=0x7fffffffd468, force=force@entry=false) at src/gcodeExport.cpp:309
309	{
(gdb) continue
Continuing.
G1 F2700 E-4.50000

Breakpoint 2, cura::GCodeExport::writeRetraction (this=this@entry=0x7fffffffd468, force=force@entry=false) at src/gcodeExport.cpp:321
321	            estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount - retractionAmount), currentSpeed);
(gdb) step
Position (e=-4.5, z=0, y=0, x=0, this=0x7fffffffd3a0) at src/timeEstimate.h:25
25	        Position(double x, double y, double z, double e) {axis[0] = x;axis[1] = y;axis[2] = z;axis[3] = e;}
(gdb) step
cura::GCodeExport::writeRetraction (this=this@entry=0x7fffffffd468, force=force@entry=false) at src/gcodeExport.cpp:321
321	            estimateCalculator.plan(TimeEstimateCalculator::Position(INT2MM(currentPosition.x), INT2MM(currentPosition.y), INT2MM(currentPosition.z), extrusionAmount - retractionAmount), currentSpeed);
(gdb) step
TimeEstimateCalculator::plan (this=this@entry=0x7fffffffd690, newPos=..., feedrate=45) at src/timeEstimate.cpp:93
93	{
(gdb) list
88	    block->initial_feedrate = initial_feedrate;
89	    block->final_feedrate = final_feedrate;
90	}                    
91	
92	void TimeEstimateCalculator::plan(Position newPos, double feedrate)
93	{
94	    Block block;
95	    memset(&block, 0, sizeof(block));
96	    
97	    block.maxTravel = 0;

You can of course use 'print' in between stepping in order to analyze the value of a variable, or see if it changed according to your expectations, etc...

Shortcut
Most (or all) of the gdb commands will have a short version, here is a list of shortcuts when running commands in the gdb prompt :

r = run
c = continue
n = next
s = step
p = print
bt = backtrace
f = frame

Pressing enter without entering a command will automatically re-run the previous command, which can be useful in order to step through multiple lines of code by entering 'n' once then pressing enter as many times as necessary.

Help
GDB has a lot of integrated help (pseudo man pages), so you can always run the command 'help' to get a list of help topics, or 'help breakpoints' or 'help list', etc.. in order to get help on a specific command and its usage.

Last Author
nickthetait
Projects
None
Subscribers
None