GDB调试(二)

GDB调试

运行中程序GDB调试

测试程序

//test2.c
//功能:从0开始每秒打印
#include <stdio.h>
#include <unistd.h>
int aaa();
int bbb(int n);
int main()
{
    aaa();
}

int aaa()
{
    bbb(0);
}

int bbb(int n)
{
    for(int i = n; i < n+10000; i++)
    {
        printf("i:%d\n", i);
        n++;
        sleep(1);
    }
}
gcc -g -o test2 test2.c
./test2
//结果
:0
i:1
i:2
i:3
i:4
i:5
i:6
i:7
......

操作步骤

1.运行程序

在终端窗口运行编译好的程序

./test2

2.进程ID

在另一个终端窗口,使用ps命令找到正在运行程序的进程ID,其中进程ID是第二个

ps aux | grep test2
//结果如下,其中正在运行程序的进程ID是15554
username   15554  0.0  0.0   2776  1408 pts/1    S+   22:38   0:00 ./test2
username   15557  0.0  0.0  12192  2432 pts/2    S+   22:39   0:00 grep --color=auto test

3.附加GDB

使用GDB附加到正在运行的程序上

sudo gdb test2 -p 15554

这里的sudo加不加看环境,有些不用加

4.GDB调试

在GDB中,你可以使用常用的调试命令如bt(查看调用堆栈),print(打印变量值),continue(继续执行程序),等等

这里因为sleep延时,直接continue后,test2继续运行,gdb这里卡住了,可以用CTRL-C重新中断

5.结束调试

命令 解析
detach 直接使用detach命令,可以从进程中分离GDB并让程序继续运行
attach PID 重新将GDB附加到某个进程上

GDB调试多进程

测试程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
        printf("begin\n");

        if ( fork() != 0 )
        {
                printf("我是父进程:进程pid=%d,父进程ppid=%d\n",getpid(),getppid());

                int ii;
                for(ii=0; ii<200; ii++)
                {
                        printf("父进程ii=%d\n",ii);
                        sleep(1);
                }
                exit(0);
                }

        else
        {
                printf("我是子进程:进程pid=%d,父进程ppid=%d\n",getpid(),getppid());
                int jj;
                for(jj=0; jj<200; jj++)
                {
                        printf("子进程jj=%d\n",jj);
                        sleep(1);
                }
                exit(0);
        }
}
gcc -g -o test3 test3.c
./test3
//运行结果
我是父进程:进程pid=5003,父进程ppid=4675
父进程ii=0
我是子进程:进程pid=5004,父进程ppid=5003
子进程jj=0
父进程ii=1
子进程jj=1
子进程jj=2
父进程ii=2
子进程jj=3
父进程ii=3
子进程jj=4
父进程ii=4
父进程ii=5
子进程jj=5
子进程jj=6
父进程ii=6
子进程jj=7
父进程ii=7
父进程ii=8
子进程jj=8
......


//开始调试
gdb test3

调试命令

命令 解析
set follow-fork-mode child 设置追踪子进程。gdb 默认调试的是父进程,如果想调试子进程,那么在用gdb调试的时候要增加。该命令要在子进程运行前设置
set detach-on-fork on/off 继续其他进程/停止其他进程。当我们调试某个进程的时候,设置别的进程是否运行。默认是on,表示调试当前进程的时候,其他的进程继续运行。off,表示调试当前进程,其他的进程被 gdb 挂起。
info inferior 通过查看可以调试的进程,方便后面切换进程
inferior processNum 可以通过 info inferior 来查看可以调试的进程,当需要切换调试的进程时,根据processNum进行切换。这里的processNum是gdb自己排的进程编号,不是进程ID

以下程序未设置命令,因此默认调试父进程,并且调试的时候子进程是运行的

eading symbols from test3...
(gdb) b 7
Breakpoint 1 at 0x1216: file test3.c, line 7.
(gdb) run
Starting program: /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main () at test3.c:7
7            printf("begin\n");
(gdb) n
begin
9            if ( fork() != 0 )
(gdb) n
[Detaching after fork from child process 5242]
我是子进程:进程pid=5242,父进程ppid=5239
子进程jj=0
11                    printf("我是父进程:进程pid=%d,父进程ppid=%d\n",getpid(),getppid());
(gdb) 子进程jj=1
子进程jj=2
子进程jj=3
子进程jj=4
子进程jj=5
子进程jj=6

使用set follow-fork-mode child命令调试子进程,并使用set detach-on-fork off命令挂起父进程,随后再切换调试父进程

Reading symbols from test3...
(gdb) b 7
Breakpoint 1 at 0x1216: file test3.c, line 7.
(gdb) set follow-fork-mode child
(gdb) set detach-on-fork off
(gdb) r
Starting program: /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main () at test3.c:7
7            printf("begin\n");
(gdb) n
begin
9            if ( fork() != 0 )
(gdb) n
[Attaching after Thread 0x7ffff7fa9740 (LWP 5333) fork to child process 5336]
[New inferior 2 (process 5336)]
Reading symbols from /usr/lib/debug/.build-id/49/0fef8403240c91833978d494d39e537409b92e.debug...
Reading symbols from /usr/lib/debug/.build-id/41/86944c50f8a32b47d74931e3f512b811813b64.debug...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Switching to Thread 0x7ffff7fa9740 (LWP 5336)]
main () at test3.c:9
9            if ( fork() != 0 )
(gdb) n
24                    printf("我是子进程:进程pid=%d,父进程ppid=%d\n",getpid(),getppid());
(gdb) n
我是子进程:进程pid=5336,父进程ppid=5333
26                    for(jj=0; jj<200; jj++)
(gdb) n
28                            printf("子进程jj=%d\n",jj);
(gdb) n
子进程jj=0
29                            sleep(1);
(gdb) n
26                    for(jj=0; jj<200; jj++)
(gdb) info inferior
  Num  Description       Connection           Executable        
  1    process 5333      1 (native)           /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3 
* 2    process 5336      1 (native)           /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3 
(gdb) inferior 1
[Switching to inferior 1 [process 5333] (/home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3)]
[Switching to thread 1.1 (Thread 0x7ffff7fa9740 (LWP 5333))]
#0  arch_fork (ctid=0x7ffff7fa9a10) at ../sysdeps/unix/sysv/linux/arch-fork.h:52
52    ../sysdeps/unix/sysv/linux/arch-fork.h: 没有那个文件或目录.
(gdb) info inferior
  Num  Description       Connection           Executable        
* 1    process 5333      1 (native)           /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3 
  2    process 5336      1 (native)           /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3 
(gdb) n
56    in ../sysdeps/unix/sysv/linux/arch-fork.h
(gdb) n
__GI__Fork () at ../sysdeps/nptl/_Fork.c:29
29    ../sysdeps/nptl/_Fork.c: 没有那个文件或目录.
(gdb) n
50    in ../sysdeps/nptl/_Fork.c
(gdb) n
__libc_fork () at ./posix/fork.c:75
75    ./posix/fork.c: 没有那个文件或目录.
(gdb) n
113    in ./posix/fork.c
(gdb) n
126    in ./posix/fork.c
(gdb) n
128    in ./posix/fork.c
(gdb) n
132    in ./posix/fork.c
(gdb) n
main () at test3.c:9
9            if ( fork() != 0 )
(gdb) n
11                    printf("我是父进程:进程pid=%d,父进程ppid=%d\n",getpid(),getppid());
(gdb) n
我是父进程:进程pid=5333,父进程ppid=5328
14                    for(ii=0; ii<200; ii++)
(gdb) n
16                            printf("父进程ii=%d\n",ii);
(gdb) n
父进程ii=0
17                            sleep(1);
(gdb) n
n14                    for(ii=0; ii<200; ii++)
(gdb) n
16                            printf("父进程ii=%d\n",ii);
(gdb) 

GDB调试多线程

测试程序

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

#define MAX 50
//全局变量
int number;
//创建一把互斥锁
pthread_mutex_t mutex;

void* funcA_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        pthread_mutex_lock(&mutex);
        number++;
        printf("Thread A, id = %lu, number = %d\n", pthread_self(), number);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    return NULL;
}

void* funcB_num(void* arg)
{
    for(int i=0; i<MAX; ++i)
    {
        pthread_mutex_lock(&mutex);
    number++;
        printf("Thread B, id = %lu, number = %d\n", pthread_self(), number);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    return NULL;
}

int main(int argc, const char* argv[])
{
    pthread_t p1, p2;

    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    // 创建两个子线程
    pthread_create(&p1, NULL, funcA_num, NULL);
    pthread_create(&p2, NULL, funcB_num, NULL);

    for(int k = 0; k < MAX; k++)
    {
        pthread_mutex_lock(&mutex);
    number++;
        printf("Thread Main, id = %lu, number = %d\n", pthread_self(), number);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }

    // 阻塞,资源回收
    pthread_join(p1, NULL);
    pthread_join(p2, NULL);

    // 销毁互斥锁
    // 线程销毁之后, 再去释放互斥锁
    pthread_mutex_destroy(&mutex);

    return 0;
}
gcc -g -o test4 test4.c -lphtread
./test4
//运行结果
Thread Main, id = 139380790110016, number = 1
Thread A, id = 139380784105024, number = 2
Thread B, id = 139380773619264, number = 3
Thread A, id = 139380784105024, number = 4
Thread B, id = 139380773619264, number = 5
Thread Main, id = 139380790110016, number = 6
......

线程查看命令

命令 解析
ps -aL | grep xxx 查看轻量级进程。轻量级的进程就是线程:p——process:进程; s——state:状态;a——all:全部;L——light:轻
pstree -p 主线程id 主线程和子线程的关系用树形展开
XX@XX-virtual-machine:~/Desktop/fatenone/project/gcc/gdb2$ ps -aL|grep test4
   4523    4523 pts/0    00:00:00 test4
   4523    4524 pts/0    00:00:00 test4
   4523    4525 pts/0    00:00:00 test4
XX@XX-virtual-machine:~/Desktop/fatenone/project/gcc/gdb2$ pstree -p 4523
test4(4523)─┬─{test4}(4524)
            └─{test4}(4525)

调试命令

命令 解析
info threads 查看线程
thread 线程号 切换到某个线程,这个线程号可以查看info threads
set scheduler-locking on 可以用来锁定当前线程,只观察这个线程的运行情况。当锁定这个线程时,其他线程就处于了暂停状态,也就是说你在当前线程执行 next、step、until、finish、return 命令时,其他线程是不会运行的。需要注意的是,你在使用 set scheduler-locking on/step 选项时要确认下当前线程是否是你期望锁定的线程,如果不是,可以使用 thread <线程编号> 切换到你需要的线程,然后再进行锁定。
set scheduler-locking step 也是用来锁定当前线程,当且仅当使用 next 或 step 命令做单步调试时会锁定当前线程,如果使用 until、finish、return 等线程内的非单步调试命令,其他线程还是有机会运行的。相比较 on 选项值,step 选项值给为单步调试提供了更加精细化的控制,因为通常我们只希望在单步调试时,不希望其他线程对当前调试的各个变量值造成影响。
set scheduler-locking off 关闭锁定当前线程,所有的线程都会执行。
opyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from test4...
(gdb) b 54
Breakpoint 1 at 0x1448: file test4.c, line 54.
(gdb) r
Starting program: /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test4 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff7a00640 (LWP 4930)]
Thread A, id = 140737347847744, number = 1
[New Thread 0x7ffff7000640 (LWP 4931)]
Thread B, id = 140737337361984, number = 2
Thread Main, id = 140737353783104, number = 3

Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54            sleep(1);
(gdb) info threads
  Id   Target Id                                Frame 
* 1    Thread 0x7ffff7fa9740 (LWP 4927) "test4" main (argc=1, 
    argv=0x7fffffffdee8) at test4.c:54
  2    Thread 0x7ffff7a00640 (LWP 4930) "test4" 0x00007ffff7ce57f8 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, 
    req=req@entry=0x7ffff79ffde0, rem=rem@entry=0x7ffff79ffde0)
    at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
  3    Thread 0x7ffff7000640 (LWP 4931) "test4" 0x00007ffff7ce57f8 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, 
    req=req@entry=0x7ffff6fffde0, rem=rem@entry=0x7ffff6fffde0)
    at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff7a00640 (LWP 4930))]
#0  0x00007ffff7ce57f8 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, 
    flags=flags@entry=0, req=req@entry=0x7ffff79ffde0, 
    rem=rem@entry=0x7ffff79ffde0)
    at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
78    ../sysdeps/unix/sysv/linux/clock_nanosleep.c: 没有那个文件或目录.
(gdb) info threads
  Id   Target Id                                Frame 
  1    Thread 0x7ffff7fa9740 (LWP 4927) "test4" main (argc=1, 
    argv=0x7fffffffdee8) at test4.c:54
* 2    Thread 0x7ffff7a00640 (LWP 4930) "test4" 0x00007ffff7ce57f8 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, 
    req=req@entry=0x7ffff79ffde0, rem=rem@entry=0x7ffff79ffde0)
    at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
  3    Thread 0x7ffff7000640 (LWP 4931) "test4" 0x00007ffff7ce57f8 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, 
    req=req@entry=0x7ffff6fffde0, rem=rem@entry=0x7ffff6fffde0)
    at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
(gdb) c
Continuing.
Thread A, id = 140737347847744, number = 4
Thread B, id = 140737337361984, number = 5
Thread A, id = 140737347847744, number = 6
Thread B, id = 140737337361984, number = 7
Thread Main, id = 140737353783104, number = 8
[Switching to Thread 0x7ffff7fa9740 (LWP 4927)]

Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54            sleep(1);
(gdb) set scheduler-locking on
(gdb) c
Continuing.
Thread Main, id = 140737353783104, number = 9//可以看到此时除了main线程,线程A和线程B都已经锁住了

Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54            sleep(1);
(gdb) set scheduler-locking off
(gdb) c
Continuing.
Thread A, id = 140737347847744, number = 10
Thread B, id = 140737337361984, number = 11
Thread B, id = 140737337361984, number = 12
Thread A, id = 140737347847744, number = 13
Thread Main, id = 140737353783104, number = 14//使用set scheduler-lockingoff命令,A和B线程不会被锁住

Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54            sleep(1);
(gdb) n
Thread B, id = 140737337361984, number = 15
Thread A, id = 140737347847744, number = 16
48        for(int k = 0; k < MAX; k++)
(gdb) n
Thread A, id = 140737347847744, number = 17
Thread B, id = 140737337361984, number = 18
50            pthread_mutex_lock(&mutex);
(gdb) n
51        number++;
(gdb) n
52            printf("Thread Main, id = %lu, number = %d\n", pthread_self(), number);
(gdb) n
Thread Main, id = 140737353783104, number = 19
53            pthread_mutex_unlock(&mutex);
(gdb) set scheduler-locking step
(gdb) c
Continuing.
Thread A, id = 140737347847744, number = 20
Thread B, id = 140737337361984, number = 21//使用set scheduler-lockingstep命令,continue的时候A和B线程没有被锁住

Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54            sleep(1);
(gdb) n
48        for(int k = 0; k < MAX; k++)
(gdb) n
50            pthread_mutex_lock(&mutex);
(gdb) n
51        number++;
(gdb) n
52            printf("Thread Main, id = %lu, number = %d\n", pthread_self(), number);
(gdb) n
Thread Main, id = 140737353783104, number = 22//使用set scheduler-lockingstep命令,next的时候A和B线程被锁住
53            pthread_mutex_unlock(&mutex);
(gdb) n

Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54            sleep(1);
(gdb) 

Core Dump基础

有时候写程序会出现 coredump 的错误,也就是内存溢出,程序挂掉。要去查看是程序中的那个地方导致了内存溢出,可以在GDB调试的时候加上 core 文件,core 文件里面记录了程序挂掉的一些重要信息。

调试命令

命令 解析
ulimt -a 看系统参数,core文件的默认大小是 0
ulimit -c unlimited 将core文件的大小修改为无限制

引用

gdb调试正在运行中的程序_gdb调试正在运行的程序-CSDN博客

深入理解GDB调试:多进程、多线程及核心文件剖析-CSDN博客

GDB-2——GDB调试多线程 - Hello-World3 - 博客园

Linux多线程调试没那么难,别就会一个printf! - 知乎

posted @   随机人生  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
点击右上角即可分享
微信分享提示