GDB's full name is GNU Project debugger, a very powerful debugger on Unix-like systems. Here's a simple example (insert algorithm) that shows how to debug with GDB, especially how to efficiently find the dead loop through interrupts, and we can also see that after fixing the bug and recompiling, we can still go through the original GDB Session for debugging (without having to re-open a gdb), which avoids duplication of setup work, and in some restricted environments (such as some real-time or embedded systems), there is often only one Linux character interface available for debugging. In this case, you can use the job to seamlessly switch between the Code editor, the compiler (the compilation environment), and the debugger. This is also a method of efficient debugging.
Let's take a look at this insertion sorting algorithm (A.CPP), which has some errors.
A.cpp#include <stdio.h> #include <stdlib.h>int x[10];int y[10];int num_inputs;int num_y = 0;void Get_args ( int AC, char **av) { num_inputs = ac-1; for (int i = 0; i < num_inputs; i++) x[i] = atoi (av[i+1]);} void Scoot_over (int jj) {for (int k = num_y-1; k > JJ; k++) y[k] = y[k-1];} void Insert (int new_y) { if (num_y = 0) { y[0] = new_y; return; } for (int j = 0; J < Num_y; J + +) { if (New_y < y[j]) { scoot_over (j); Y[J] = new_y; return;}}} void Process_data () {for (num_y = 0; num_y < num_inputs; num_y++) Insert (x[num_y]);} void Print_results () { for (int i = 0; i < num_inputs; i++) printf ("%d\n", Y[i]);} int main (int argc, char * * argv) { Get_args (ARGC,ARGV); Process_data (); Print_results (); return 0;}
The code is not analyzed, take a little time should be able to understand. How many mistakes can you find?
To compile with GCC:
Gcc-g-wall-o Insert_sort A.cpp
"-G" tells GCC to include debug information, such as symbol table information, in a binary file so that GDB can match the address and function and variable name when debugging. When debugging, you can view its value according to the variable name, add a breakpoint to a line in the source code, etc., which is the prerequisite for debugging. "-wall" is to turn on all the warning switches so that they will print if they encounter warning at compile time. In general, it is recommended to turn on all warning switches.
Run the compiled program (./insert_sort), only to find that the program does not stop at all. On the debugger! (Some bugs can be seen at a glance, using gdb just to introduce the underlying functionality)
TUI mode
The current version of GDB supports the so-called end-user interface mode (Terminal User Interface), which shows the source code while displaying the GDB command line. The advantage is that you can always see which statement is currently executed. The reason is called Tui, should be copied from the GUI. Note that you can turn TUI mode on or off by using CTRL + X + A.
Gdb-tui./insert_sort
Dead loop
Running the Run command after entering GDB, passing in the command line arguments, which is the array to sort. Of course, the program also can't stop:
In order for the program to stop, we can send an interrupt signal (CTRL + C), and GDB will suspend the debug process after it snaps to that signal. Note that when sending this interruption a little bit of finesse depends entirely on our experience and the characteristics of the program. Like this simple program, it normally executes almost immediately. If the delay is felt to indicate that a dead loop (or something else) has occurred, the interruption must fall in the loop of the dead loop. This allows us to find useful information by examining the context. Large programs If you normally need to run for a few seconds or even a few minutes, then you need to wait at least until it times out before you interrupt.
At this point, the program pauses on line 44th (line 44th has not been executed), and the 44th row in TUI mode is highlighted. We know that this line is part of some dead loop body.
Because the paused code has a certain randomness, you can run it several times to see how the statements are different for each stay. The command-line arguments ("5") are not entered when the Run command is executed, and GdB remembers. Also, when you execute run, GDB asks if you want to start the program again, and of course we have to do it from the beginning.
After basically determining the location (such as the above 44 lines), because this program is very small, you can step through the steps (step) to view the sentence. It is not difficult to find the problem in line 24th, the specific steps are omitted.
Seamless switching
In coding, debugging, unless you have an integrated development environment, you will normally need to open three windows: Code Editor (such as many people use vim), compiler (new window running GCC or make command, execute program, etc.), debugger. The integrated development environment is good, but in some cases you cannot use any GUI tools, such as some embedded devices that only provide a character interface-you can use only one Linux command line. Obviously, if you need to close vim in order to open the GCC compile command after you have modified the code in vim, or if you need to close the debugger to reopen the vim modification code, compile, and reopen the debugger, it is too painful to go through the debugging process!
Fortunately, through the Linux job Management mechanism, the current task can be suspended through Ctrl + Z, to return to the terminal to do other things. The jobs command allows you to see what tasks are currently in the shell. For example, when I paused gdb, jobs showed that my vim editor process and GDB are currently in a pending state.
The following are some of the relevant commands, more commonly used
FG%1 //Open vim,1 is vim corresponding to the job number
FG%2 //Open GDB
BG%1 //Let Vim run in the background
Kill%1 && FG//Kill the VIM process completely
GDB's "online Refresh"
Well, just introduced the seamless switch, that we can not close the GDB case (Note that CTRL + Z is not to close gdb this process, just hang) switch to vim to modify the code to eliminate the Dead loop (the 24th line of "if (num_y = 0)" Change to "if (num_y = = 0)" )。 The action sequence can be:
CTRL + Z//Hang GdB
Jobs //view Vim's corresponding job number, assuming 1
FG%1 //Enter VIM, modify code:
CTRL + Z//modify to hang vim after completion
Gcc-g-wall-o insert_sort a.cpp//RECOMPILE program
FG%2 //Enter GDB, assuming GDB's job number is 2
Now we're back to the GDB debugging interface! But there is one more step before debugging, how do I get gdb to recognize the new program (because the program has been recompiled)? Just run it again. Because GDB is not closed, the previously set breakpoints, the command line arguments passed in when running run, and so on are preserved and do not need to be re-entered. Very good!
GDB automatically detects that the program has changed and reloads the symbol.
Other bugs
For the other bugs in this example, here is not much to say. Interested classmates, can discuss with me.
From an example to understand gdb and efficient debugging