gdb的使用总结


最近学习了 100个gdb技巧,总结了一些自己认为需要掌握的点。按照教程都进行了实践,相信熟练掌握之后,调试程序应该可以更有效率。

函数

1. 显示函数

(gdb)info functions

支持正则匹配

(gdb)info functions func*

2. 进入函数

  • step:会进入函数内部
  • next:不进入函数,等待函数执行完,显示下一行函数代码

3. 退出函数

  • finish:继续执行函数中剩余代码,打印返回值,显示下一行代码。
  • return:不继续执行函数中剩余代码,而是直接返回。可以直接使用return expression来更改设置返回值。

4. 直接执行函数

可以直接使用call或者print直接调用函数。

#include <stdio.h>
int global = 1;
int func(void) {
    return (++global);
}

int main(void) {
    printf("%d\n", global);
    return 0;
}
(gdb) start
Temporary breakpoint 1 at 0x40053c: file main.cpp, line 12.
Starting program: /home/xj/practice/gdb/test 

Temporary breakpoint 1, main () at main.cpp:12
12          printf("%d\n", global);
(gdb) call func
$1 = {int (void)} 0x40051d <func()>
(gdb) print func
$2 = {int (void)} 0x40051d <func()>
(gdb) n
1
13          return 0;

5. 打印函数堆栈帧信息

(gdb) info frame

6. 选择函数堆栈帧

(gdb) bt
#0  func1 (a=10) at test.c:5
#1  0x0000000000400560 in func2 (a=10) at test.c:11
#2  0x0000000000400586 in func3 (a=10) at test.c:18
#3  0x000000000040059e in main () at test.c:24
(gdb) frame 2
#2  0x0000000000400586 in func3 (a=10) at test.c:18
18              c = 2 * func2(a);

使用bt可显示函数调用的层数。当程序暂停后,可以用“frame n”命令选择函数堆栈帧,其中n是层数。

7.向上或者向下切换函数堆栈帧

(gdb) bt
#0  func1 (a=10) at test.c:5
#1  0x0000000000400560 in func2 (a=10) at test.c:11
#2  0x0000000000400586 in func3 (a=10) at test.c:18
#3  0x000000000040059e in main () at test.c:24
(gdb) frame 2
#2  0x0000000000400586 in func3 (a=10) at test.c:18
18              c = 2 * func2(a);
(gdb) up 1
#3  0x000000000040059e in main () at test.c:24
24              printf("%d\n", func3(10));
(gdb) down 2
#1  0x0000000000400560 in func2 (a=10) at test.c:11
11              c = 2 * func1(a);

当程序暂停后,可以用“up n”或“down n”命令向上或向下选择函数堆栈帧,其中n是层数。

断点

1.在匿名空间设置断点

namespace Foo
{
  void foo()
  {
  }
}

namespace
{
  void bar()
  {
  }
}

对foo函数设置断点

(gdb) b Foo::foo

对匿名空间的bar函数设置断点

(gdb) b (anonymous namespace)::bar

2.在文件号上打断点

如果要在当前文件中的某一行打断点,直接b linenum即可,例如:

(gdb) b 7

显式指定文件,b file:linenum例如:

(gdb) b file.c:6
Breakpoint 1 at 0x40053b: file.c:6. (2 locations)
(gdb) i breakpoints 
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   <MULTIPLE>         
1.1                         y     0x000000000040053b in print_a at a/file.c:6
1.2                         y     0x000000000040054b in print_b at b/file.c:6

可以看出,gdb会对所有匹配的文件设置断点。你可以通过指定(部分)路径,来区分相同的文件名:

(gdb) b a/file.c:6

3.查看断点信息

(gdb) info breakpoints

缩写i b

4.保存已经设置好的断点

(gdb) save breakpoints file-name-to-save

下次调试时,可以使用如下命令批量设置保存的断点

(gdb) source file-name-to-save

5.设置临时断点

如果想让断点只生效一次,可以使用“tbreak”命令(缩写为:tb)

6.设置条件断点

只有在条件满足时,断点才会被触发,命令是“break … if cond”

#include <stdio.h>

int main(void) {
    int i = 0;
    int sum = 0;
    for (i = 1; i <= 200; i++) {
        sum += i;
    }
    printf("%d\n", sum);
    return 0;
}

设置当i为101时给第7行设置断点

(gdb) start
Temporary breakpoint 1 at 0x4008d5: file main.cpp, line 4.
Starting program: /home/xj/practice/gdb/test 

Temporary breakpoint 1, main () at main.cpp:4
4           int i = 0;
(gdb) b 7 if i==101
Breakpoint 2 at 0x4008ec: file main.cpp, line 7.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/xj/practice/gdb/test 

Breakpoint 2, main () at main.cpp:7
7               sum += i;
(gdb) p sum
$1 = 5050

7.忽略断点

在设置断点以后,可以忽略断点,命令是“ignore bnum count”:意思是接下来count次编号为bnum的断点触发都不会让程序中断,只有第count + 1次断点触发才会让程序中断
以上面程序为例,忽略前五次断点

(gdb) b 7
Breakpoint 2 at 0x4008ec: file main.cpp, line 7.
(gdb) ignore 2 5
Will ignore next 5 crossings of breakpoint 2.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/xj/practice/gdb/test 

Breakpoint 2, main () at main.cpp:7
7               sum += i;
(gdb) p i
$1 = 6

观察点

1.设置观察点

使用“watch”命令设置观察点,也就是当一个变量值发生变化时,程序会停下来。

查看所有观察点

(gdb) info watchpoints

2.设置只针对特定线程生效的观察点

#include <pthread.h>
#include <stdio.h>

int a = 0;
void* thread1_func(void* p_arg) {
    while (1) {
        a++;
        sleep(10);
    }
}

void* thread2_func(void* p_arg) {
    while (1) {
        a++;
        sleep(10);
    }
}

int main(void) {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, thread1_func, "Thread 1");
    pthread_create(&t2, NULL, thread2_func, "Thread 2");
    sleep(1000);
    return 0;
}

gdb可以使用“watch expr thread threadnum”命令设置观察点只针对特定线程生效,也就是只有编号为threadnum的线程改变了变量的值,程序才会停下来,其它编号线程改变变量的值不会让程序停住。

(gdb) start
Temporary breakpoint 2 at 0x40068d: file main.c, line 21.
Starting program: /home/xj/practice/gdb/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 2, main () at main.c:21
warning: Source file is more recent than executable.
21	    pthread_create(&t1, NULL, thread1_func, "Thread 1");
(gdb) c
Continuing.
[New Thread 0x7ffff77ef700 (LWP 26541)]
[New Thread 0x7ffff6fee700 (LWP 26542)]

Thread 1 "test" hit Breakpoint 1, main () at main.c:23
23	    sleep(1000);
(gdb) info thread
  Id   Target Id         Frame 
* 1    Thread 0x7ffff7fd2700 (LWP 26517) "test" main () at main.c:23
  2    Thread 0x7ffff77ef700 (LWP 26541) "test" 0x00007ffff78bc38d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
  3    Thread 0x7ffff6fee700 (LWP 26542) "test" 0x00007ffff78bc38d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
(gdb) wa a thread 2
Hardware watchpoint 3: a
(gdb) c
Continuing.
[Switching to Thread 0x7ffff77ef700 (LWP 26541)]

Thread 2 "test" hit Hardware watchpoint 3: a

Old value = 27
New value = 28
thread1_func (p_arg=0x400764) at main.c:8
8	        sleep(10);

3.设置读观察点

gdb可以使用“rwatch”命令设置读观察点,也就是当发生读取变量行为时,程序就会暂停住。用上面程序为例:

(gdb) rw a
Hardware read watchpoint 1: a
(gdb) start
Temporary breakpoint 2 at 0x40068d: file main.c, line 21.
Starting program: /home/xj/practice/gdb/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 2, main () at main.c:21
warning: Source file is more recent than executable.
21	    pthread_create(&t1, NULL, thread1_func, "Thread 1");
(gdb) c
Continuing.
[New Thread 0x7ffff77ef700 (LWP 5063)]
[New Thread 0x7ffff6fee700 (LWP 5064)]
[Switching to Thread 0x7ffff77ef700 (LWP 5063)]

Thread 2 "test" hit Hardware read watchpoint 1: a

Value = 0
0x000000000040063f in thread1_func (p_arg=0x400764) at main.c:7
7	        a++;

4.设置读写观察点

gdb可以使用“awatch”命令设置读写观察点,也就是当发生读取变量或改变变量值的行为时,程序就会暂停住。
缩写aw

打印

1.打印字符串

#include <stdio.h>
#include <wchar.h>

int main(void)
{
        char str1[] = "abcd";
        wchar_t str2[] = L"abcd";
        
        return 0;
}

用gdb调试程序时,可以使用“x/s”命令打印ASCII字符串。
打印宽字符字符串时,要根据宽字符的长度决定如何打印:

(gdb) start
Temporary breakpoint 1 at 0x4005f5: file main.c, line 5.
Starting program: /home/xj/practice/gdb/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 1, main () at main.c:5
5	{
(gdb) n
6	        char str1[] = "abcd";
(gdb) n
7	        wchar_t str2[] = L"abcd";
(gdb) x/s str1
0x7fffffffda90:	"abcd"
(gdb) n
9	        return 0;
(gdb) p sizeof(wchar_t)
$1 = 4
(gdb) x/ws str2
0x7fffffffda70:	U"abcd

由于当前平台宽字符的长度为4个字节,则用“x/ws”命令。如果是2个字节,则用“x/hs”命令。

测试可以直接使用print

2.打印stl内容

#include <iostream>
#include <vector>

using namespace std;
int main() {
    vector<int> vec(10);  // 10 zero-initialized elements
    for (int i = 0; i < vec.size(); i++)
        vec[i] = i;
    cout << "vec contains:";
    for (int i = 0; i < vec.size(); i++)
        cout << ' ' << vec[i];
    cout << '\n';
    return 0;
}

gdb 7.0以后可以直接打印,p vec打印所有。p vec[1]打印某个元素。

(gdb) start
Temporary breakpoint 2 at 0x400a26: file main.cpp, line 6.
Starting program: /home/xj/practice/gdb/test 

Temporary breakpoint 2, main () at main.cpp:6
6	    vector<int> vec(10);  // 10 zero-initialized elements
(gdb) c
Continuing.

Breakpoint 1, main () at main.cpp:9
9	    cout << "vec contains:";
(gdb) print vec
$1 = std::vector of length 10, capacity 10 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
(gdb) print vec[1]
$2 = 1
(gdb) print vec[6]
$3 = 6
(gdb) print vec[6]@1
$4 = {6}
(gdb) print vec[6]@3
$5 = {6, 7, 8}

使用p vec[start]@len可以打印从start索引开始,连续len个元素。

3.打印大数组

要打印大数组的内容,缺省最多会显示200个元素。
可以使用如下命令,设置这个最大限制数:

(gdb) set print elements number-of-elements

也可以使用如下命令,设置为没有限制:

(gdb) set print elements 0

4.打印数组下标

在gdb中,当打印一个数组时,缺省是不打印索引下标的:

(gdb) print vec
$1 = std::vector of length 10, capacity 10 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

如果要打印索引下标,则可以通过如下命令进行设置:

(gdb) set print array-indexes on
(gdb) p vec
$6 = std::vector of length 10, capacity 10 = {[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 5, [6] = 6, [7] = 7, [8] = 8, [9] = 9}

5.打印局部变量

#include <stdio.h>

void fun_a(void) {
    int a = 0;
    printf("%d\n", a);
}

void fun_b(void) {
    int b = 1;
    fun_a();
    printf("%d\n", b);
}

void fun_c(void) {
    int c = 2;
    fun_b();
    printf("%d\n", c);
}

void fun_d(void) {
    int d = 3;
    fun_c();
    printf("%d\n", d);
}

int main(void) {
    int var = -1;
    fun_d();
    return 0;
}

首先我们在函数fun_a里打上断点,当程序断住时,显示调用栈信息:

(gdb) bt
#0  fun_a () at a.c:6
#1  0x000109b0 in fun_b () at a.c:12
#2  0x000109e4 in fun_c () at a.c:19
#3  0x00010a18 in fun_d () at a.c:26
#4  0x00010a4c in main () at a.c:33

接下来,用“bt full”命令显示各个函数的局部变量值:

(gdb) bt full
#0  fun_a () at a.c:6
        a = 0
#1  0x000109b0 in fun_b () at a.c:12
        b = 1
#2  0x000109e4 in fun_c () at a.c:19
        c = 2
#3  0x00010a18 in fun_d () at a.c:26
        d = 3
#4  0x00010a4c in main () at a.c:33
        var = -1

如果只显示当前函数的局部变量

(gdb) info locals
a = 0

测试发现在执行到func_a,直接使用print a可以打印出来。

6.打印静态变量

直接打印变量名可能并不是想要的,可以显示指定文件名

(gdb) p 'static-1.c'::var
$1 = 1

7.打印变量类型

#include <stdio.h>

struct child {
    char name[10];
    enum { boy, girl } gender;
};

struct child he = {"Tom", boy};

int main(void) {
    static struct child she = {"Jerry", girl};
    printf("Hello %s %s.\n", he.gender == boy ? "boy" : "girl", he.name);
    printf("Hello %s %s.\n", she.gender == boy ? "boy" : "girl", she.name);
    return 0;
}

可以使用如下命令查看变量的类型:

(gdb) whatis he
type = struct child
(gdb) ptype child
No symbol "child" in current context.

查看详细的类型信息:

(gdb) ptype he
type = struct child {
    char name[10];
    enum {boy, girl} gender;
}

查看定义该变量的文件:

(gdb) i variables he
All variables matching regular expression "he":

File variable.c:
struct child he;

会显示所有包含(匹配)该表达式的变量。如果只想查看完全匹配给定名字的变量:

(gdb) i variables ^he$
All variables matching regular expression "^he$":

File variable.c:
struct child he;

8.打印源码

如上所示,在gdb中可以使用list(简写为l)命令来显示源代码以及行号。list命令可以指定行号,函数:

(gdb) l 24
(gdb) l main

还可以指定向前或向后打印:

(gdb) l -
(gdb) l +

还可以指定范围:

(gdb) l 1,10

9.打印结构体成员

可以执行“set print pretty on”命令,这样每行只会显示结构体的一名成员,而且还会根据成员的定义层次进行缩进:

(gdb) print he
$1 = {name = "Tom\000\000\000\000\000\000", gender = boy}
(gdb) set print pretty on
(gdb) print he
$2 = {
  name = "Tom\000\000\000\000\000\000", 
  gender = boy
}

10.打印调用帧中变量的值

#include <stdio.h>

int func1(int a) {
    int b = 1;
    return b * a;
}

int func2(int a) {
    int b = 2;
    return b * func1(a);
}

int func3(int a) {
    int b = 3;
    return b * func2(a);
}

int main(void) {
    printf("%d\n", func3(10));
    return 0;
}

如果想查看调用栈帧中的变量,可以先切换到该栈帧中,然后打印:

(gdb) bt
#0  func1 (a=10) at main.cpp:4
#1  0x0000000000400550 in func2 (a=10) at main.cpp:10
#2  0x0000000000400572 in func3 (a=10) at main.cpp:15
#3  0x0000000000400586 in main () at main.cpp:19
(gdb) f 1
#1  0x0000000000400550 in func2 (a=10) at main.cpp:10
10	    return b * func1(a);
(gdb) p b
$1 = 2
(gdb) f 2
#2  0x0000000000400572 in func3 (a=10) at main.cpp:15
15	    return b * func2(a);
(gdb) p b

也可以不进行切换,直接打印:

(gdb) p func2::b
$1 = 2
(gdb) p func3::b
$2 = 3

多进程/线程

1.调试已经启动进程

找到进程的pid

(gdb) attach pid

2.调试子进程

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(void) {
    pid_t pid;
    pid = fork();
    if (pid < 0) {
        exit(1);
    } else if (pid > 0) {
        exit(0);
    }
    printf("hello world\n");
    return 0;
}

在调试多进程程序时,gdb默认会追踪父进程

(gdb) start
Temporary breakpoint 1 at 0x400655: file main.c, line 8.
Starting program: /home/xj/practice/gdb/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 1, main () at main.c:8
8	    pid = fork();
(gdb) n
hello world
9	    if (pid < 0) {
(gdb) 
11	    } else if (pid > 0) {
(gdb) 
12	        exit(0);
(gdb) 
[Inferior 1 (process 10405) exited normally]

如果要调试子进程,要使用如下命令:“set follow-fork-mode child”,例如:

(gdb) set follow-fork-mode child
(gdb) n
[New process 15951]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Switching to Thread 0x7ffff7fd2700 (LWP 15951)]
main () at main.c:9
9	    if (pid < 0) {
(gdb) n
11	    } else if (pid > 0) {
(gdb) n
14	    printf("hello world\n");
(gdb) n
hello world
15	    return 0;

可以看到最后打印了hello world。对应pid为0表示子进程。

3.同时调试父进程和子进程

如果要同时调试父进程和子进程,可以使用“set detach-on-fork off”(默认detach-on-fork是on)命令,这样gdb就能同时调试父子进程,并且在调试一个进程时,另外一个进程处于挂起状态。

4.查看线程信息

(gdb) info thread
  Id   Target Id         Frame 
* 1    Thread 0x7ffff7fd2700 (LWP 22355) "test" main () at main.c:15
  2    Thread 0x7ffff77ef700 (LWP 22400) "test" 0x00007ffff78bc38d in nanosleep
    () at ../sysdeps/unix/syscall-template.S:84
  3    Thread 0x7ffff6fee700 (LWP 22410) "test" 0x00007ffff78bc38d in nanosleep
    () at ../sysdeps/unix/syscall-template.S:8

第一项(Id):是gdb标示每个线程的唯一ID:1,2等等。
第二项(Target Id):是具体系统平台用来标示每个线程的ID,不同平台信息可能会不同。 像当前Linux平台显示的就是: Thread 0x7ffff7fd2700 (LWP 22355)。
第三项(Frame):显示的是线程执行到哪个函数。
前面带“*”表示的是“current thread”,可以理解为gdb调试多线程程序时,选择的一个“默认线程”。

使用"thread thread-id"实现不同线程之间的切换,查看指定线程的堆栈信息

(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff77ef700 (LWP 22400))]
#0  0x00007ffff78bc38d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
84	../sysdeps/unix/syscall-template.S: No such file or directory.

使用"thread apply [thread-id-list] [all] args"可以在多个线程上执行命令,例如:thread apply all bt可以查看所有线程的堆栈信息

(gdb) thread apply all bt

Thread 3 (Thread 0x7ffff6fee700 (LWP 22410)):
#0  0x00007ffff78bc38d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
#1  0x00007ffff78bc2da in __sleep (seconds=0) at ../sysdeps/posix/sleep.c:55
#2  0x00000000004006a4 in thread_func (p_arg=0x40078d) at main.c:6
#3  0x00007ffff7bc16ba in start_thread (arg=0x7ffff6fee700)
    at pthread_create.c:333
#4  0x00007ffff78f74dd in clone ()
    at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109

Thread 2 (Thread 0x7ffff77ef700 (LWP 22400)):
#0  0x00007ffff78bc38d in nanosleep () at ../sysdeps/unix/syscall-template.S:84
#1  0x00007ffff78bc2da in __sleep (seconds=0) at ../sysdeps/posix/sleep.c:55
#2  0x00000000004006a4 in thread_func (p_arg=0x400784) at main.c:6
#3  0x00007ffff7bc16ba in start_thread (arg=0x7ffff77ef700)
    at pthread_create.c:333
#4  0x00007ffff78f74dd in clone ()
    at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109

Thread 1 (Thread 0x7ffff7fd2700 (LWP 22355)):
#0  main () at main.c:15

5.只允许一个线程运行

#include <pthread.h>
#include <stdio.h>
int a = 0;
int b = 0;
void* thread1_func(void* p_arg) {
    while (1) {
        a++;
        sleep(1);
    }
}

void* thread2_func(void* p_arg) {
    while (1) {
        b++;
        sleep(1);
    }
}

int main(void) {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, thread1_func, "Thread 1");
    pthread_create(&t2, NULL, thread2_func, "Thread 2");
    sleep(1000);
    return;
}

gdb调试多线程时,一旦程序断住,所以线程都停住。当调试一个线程时,使用s或者n,所有线程都同时执行。

(gdb) b 7
Breakpoint 1 at 0x400639: file main.c, line 7.
(gdb) r
Starting program: /home/xj/practice/gdb/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff77ef700 (LWP 6868)]
[New Thread 0x7ffff6fee700 (LWP 6869)]
[Switching to Thread 0x7ffff77ef700 (LWP 6868)]

Thread 2 "test" hit Breakpoint 1, thread1_func (p_arg=0x400764) at main.c:7
7	        a++;
(gdb) p b
$1 = 1
(gdb) n
8	        sleep(1);
(gdb) 
9	    }
(gdb) p b
$2 = 4
(gdb) n

Thread 2 "test" hit Breakpoint 1, thread1_func (p_arg=0x400764) at main.c:7
7	        a++;
(gdb) p b
$3 = 5

thread1_func更新全局变量a的值,thread2_func更新全局变量b的值。我在thread1_func里a++语句打上断点,当断点第一次命中时,打印b的值是0,在单步调试thread1_func几次后,b的值变成3,证明在单步调试thread1_func时,thread2_func也在执行。
如果想在调试一个线程时,让其它线程暂停执行,可以使用“set scheduler-locking on”:

(gdb) start
Temporary breakpoint 2 at 0x40068d: file main.c, line 21.
Starting program: /home/xj/practice/gdb/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 2, main () at main.c:21
21	    pthread_create(&t1, NULL, thread1_func, "Thread 1");
(gdb) n
[New Thread 0x7ffff77ef700 (LWP 9935)]
22	    pthread_create(&t2, NULL, thread2_func, "Thread 2");
(gdb) n
[Switching to Thread 0x7ffff77ef700 (LWP 9935)]

Thread 2 "test" hit Breakpoint 1, thread1_func (p_arg=0x400764) at main.c:7
7	        a++;
(gdb) set scheduler-locking on
(gdb) p b
$1 = 0
(gdb) n
8	        sleep(1);
(gdb) p b
$2 = 0
(gdb) n
9	    }
(gdb) p b

此外,“set scheduler-locking”命令除了支持off和on模式外(默认是off),还有一个step模式。含义是:当用"step"命令调试线程时,其它线程不会执行,但是用其它命令(比如"next")调试线程时,其它线程也许会执行。

core dump文件

1.为调试的进程产生core文件

在用gdb调试程序时,我们有时想让被调试的进程产生core dump文件,记录现在进程的状态,以供以后分析。可以用“generate-core-file”命令来产生core dump文件:

也可使用“gcore”命令

(gdb) gcore
Saved corefile core.9862

2.加载可执行文件和core dump文件

1.程序和core一起启动

gdb ./ext ./core

2.gdb启动后,动态加载可执行程序和core dump文件,这时可以用“file”和“core”(core-file命令缩写)命令。“file”命令用来读取可执行文件的符号表信息,而“core”命令则是指定core dump文件的位置

$ gdb -q
(gdb) file ./test
Reading symbols from ./test...done.
(gdb) core core.9862 
[New LWP 9935]
[New LWP 9862]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `/home/xj/practice/gdb/test'.
Program terminated with signal SIGTRAP, Trace/breakpoint trap.
#0  thread1_func (p_arg=0x400764) at main.c:9
9	    }
[Current thread is 1 (Thread 0x7ffff77ef700 (LWP 9935))]

改变程序的执行

1.改变字符串的值

#include <stdio.h>

int main(void) {
    char p1[] = "Sam";
    char* p2 = "Bob";
    printf("p1 is %s, p2 is %s\n", p1, p2);
    return 0;
}

可以用“set”命令改变字符串的值,以上面程序为例:

(gdb) n
4	    char p1[] = "Sam";
(gdb) n
5	    char* p2 = "Bob";
(gdb) n
6	    printf("p1 is %s, p2 is %s\n", p1, p2);
(gdb) set main::p1="Jil"
(gdb) set main::p2="Bill"
(gdb) n
p1 is Jil, p2 is Bill
7	    return 0;

2.设置变量的值

可以用“set var variable=expr”命令设置变量的值

(gdb) set var i = 8
(gdb) n
6	}
(gdb) 
main () at main.c:11
11	    printf("%d\n", a);
(gdb) 
8
12	    return 0;

信号

1.查看信号处理信息

(gdb) i signal
Signal        Stop	Print	Pass to program	Description

SIGHUP        Yes	Yes	Yes		Hangup
SIGINT        Yes	Yes	No		Interrupt
SIGQUIT       Yes	Yes	Yes		Quit
SIGILL        Yes	Yes	Yes		Illegal instruction
SIGTRAP       Yes	Yes	No		Trace/breakpoint trap

第一项(Signal):标示每个信号。

第二项(Stop):表示被调试的程序有对应的信号发生时,gdb是否会暂停程序。

第三项(Print):表示被调试的程序有对应的信号发生时,gdb是否会打印相关信息。

第四项(Pass to program):gdb是否会把这个信号发给被调试的程序。

第五项(Description):信号的描述信息。

从上面的输出可以看到,当SIGINT信号发生时,gdb会暂停被调试的程序,并打印相关信息,但不会把这个信号发给被调试的程序。而当SIGALRM信号发生时,gdb不会暂停被调试的程序,也不打印相关信息,但会把这个信号发给被调试的程序。

启动gdb调试上面的程序,同时另起一个终端,先后发送SIGINT和SIGALRM信号给被调试的进程,输出如下:

Program received signal SIGINT, Interrupt.
0xfeeeae55 in ___nanosleep () from /lib/libc.so.1
(gdb) c
Continuing.
Receive signal: 14

可以看到收到SIGINT时,程序暂停了,也输出了信号信息,但并没有把SIGINT信号交由进程处理(程序没有输出)。而收到SIGALRM信号时,程序没有暂停,也没有输出信号信息,但把SIGALRM信号交由进程处理了(程序打印了输出)。

使用kill命令可以向程序发送信号。

2.改变信号发生时的默认处理方式

根据上面第1点,gdb对信号有默认的处理方式。例如默认情况下,发生SIGHUP信号时,gdb会暂停程序的执行,并打印收到信号的信息。此时需要执行continue命令继续程序的执行。

可以使用“handle SIGHUP nostop”命令设置当SIGHUP信号发生时,gdb不暂停程序。
如果想恢复之前的行为,用“handle SIGHUP stop”命令即可。需要注意的是,设置stop的同时,默认也会设置print。

可以用“handle SIGHUP noprint”命令设置当SIGHUP信号发生时,gdb不打印信号信息。想恢复之前的行为,用“handle SIGHUP print”命令即可

可以用“handle SIGHUP nopass”命令设置当SIGHUP信号发生时,gdb不把信号丢给程序处理。想恢复之前的行为,用“handle SIGHUP pass”命令即可。

3.给程序发送信号

#include <signal.h>
#include <stdio.h>

void handler(int sig) {
    signal(sig, handler);
    printf("Receive signal: %d\n", sig);
}

int main(void) {
    signal(SIGHUP, handler);
    while (1) {
        sleep(1);
    }
    return 0;
}

用gdb调试程序的过程中,当被调试程序停止后,可以用“signal signal_name”命令让程序继续运行,但会立即给程序发送信号。

(gdb) r
Starting program: /home/xj/practice/gdb/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
^C
Program received signal SIGINT, Interrupt.
0x00007ffff78bc370 in __nanosleep_nocancel ()
    at ../sysdeps/unix/syscall-template.S:84
84	../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) signal SIGHUP
Continuing with signal SIGHUP.
Receive signal: 1
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值