首页
学习
活动
专区
圈层
工具
发布
50 篇文章
1
C语言中如何实现数据帧封装与解析
2
【熟视C语言】如何快速的了解一个库函数(C语言讲解,以string.h中的部分库函数为例)
3
C语言代码封装MQTT协议报文,了解MQTT协议通信过程
4
NV12数据格式转H265编码格式实现过程
5
基于Modbus协议实现Openplc与Kingview的仿真通讯与模拟测试
6
onvif协议最新版本_接口协议测试工具
7
linux后台开发常用调试工具
8
C/C++开发人员要了解的几大著名C/C++开源库[通俗易懂]
9
适用于嵌入式环境的加速计算库
10
Linux下WebRTC框架Janus编译过程
11
探索嵌入式应用框架(EAF)
12
[C&C++]联合体union的特征及用其进行传输
13
联合体和结构体一起解析数据
14
国标GB28181协议客户端开发(四)实时视频数据传输
15
6.1 C/C++ 封装字符串操作
17
C语言进阶——自定义类型
18
干货 | 结构体、联合体嵌套使用的一些实用操作
19
C语言的面向对象编程
20
QT应用编程: 编写低功耗BLE蓝牙调试助手(Android系统APP)
21
设计模式之接口隔离原则C++实现
22
嵌入式软件开发的框架思维
23
通过面向对象设计串口协议
24
QT应用编程: 开发串口调试助手
25
一种高效的串口自定义16进制通信协议的嵌入式应用开发解决方案
26
嵌入式中状态机的几种骚操作
27
【干货】用FreeRTOS搭建Event-Driven应用框架
28
嵌入式开发基础之任务管理(线程管理)
29
SIP菜鸟如何学SIP
30
Linux下使用libuvc读取控制USB免驱摄像头
31
Linux 使用strace命令查找进程卡死原因
32
84-OOP之组合
33
如何调试多线程程序
34
GDB多线程调试分析
35
GDB多线程多进程调试
36
一个简单实用的线程基类
37
OpenThread是世界上最舒心的跨平台多线程并发库
38
OpenMiniServer是一个超迷你、 超易用的C++高并发跨平台服务器框架
39
OpenSocket是跨全平台的高性能高并发网络库
40
一个C++多线程TCP服务Demo
41
一文搞懂网络库的分层设计!
42
实现一个接收多路RTP流,输出一路RTMP流的简单MCU
43
谈谈嵌入式应用软件人机界面开发的菜单框架编写
44
union 的概念及在嵌入式编程中的应用
45
让终端支持https,移植OpenSSL和libcurl到嵌入式linux,遇到的问题总结
46
日常工作中的设计:解耦和封装
47
一种简易的嵌入式设备系统日志记录方法
48
PLC和计算机通信的数据采集方法和传输监控的实现(1)
49
C++随笔(五)三种实现串口通信的方式
50
开源一个自己写过的MQTT 客户端调试工具

GDB多线程多进程调试

gdb多线程调试

gdb提供的多线程调试工具

  1. 新线程创建自动提醒
  2. thread thread-id实现不同线程之间的切换
  3. info threads查询存在的线程
  4. thread apply [thread-id-list] [all] args在一系列线程上执行命令
  5. 线程中设置指定的断点
  6. set print thread-events控制打印线程启动或结束是的信息
  7. set scheduler-locking off|on|step在使用step或是continue进行调试的时候,其他可能也会并行的执行,如何才能够只让被调试的线程执行呢?该命令工具可以达到这个效果。
代码语言:txt
复制
off:不锁定任何线程,也就是所有的线程都执行,这是默认值。
代码语言:txt
复制
on:只有当前被调试的线程能够执行。
代码语言:txt
复制
step:阻止其他线程在当前线程单步调试时,抢占当前线程。只有当next、continue、util以及finish的时候,其他线程才会获得重新运行的机会。

示例代码

代码语言:txt
复制
#include <iostream>
#include <pthread.h>
#include <iostream>

void* threadPrintHello(void* arg)
{
    while(1)
    {
        sleep(5);
        std::cout << "hello" << std::endl;
    }
}

void* threadPrintWorld(void* arg)
{
    while(1)
    {
        sleep(5);
        std::cout << "world" << std::endl;
    }
}

int main( int argc , char* argv[])
{
    pthread_t pid_hello , pid_world;

    int ret = 0;

    ret = pthread_create(&pid_hello , NULL , threadPrintHello , NULL);

    if( ret != 0 )
    {
        std::cout << "Create threadHello error" << std::endl;
        return -1;
    }

    ret = pthread_create(&pid_world , NULL , threadPrintWorld , NULL);

    if( ret != 0 )
    {
        std::cout << "Create threadWorld error" << std::endl;
        return -1;
    }

    while(1)
    {
        sleep(10);
        std::cout << "In main thread"  << std::endl;
    }

    pthread_join(pid_hello , NULL);
    pthread_join(pid_world , NULL);

    return 0;
}

线程创建提醒

在GNU/Linux上,如果gdb检测一个新的线程,会给出如下通知

代码语言:txt
复制
[New Thread 0x7ffff708b700 (LWP 20567)]
[New Thread 0x7ffff688a700 (LWP 20568)]

查询已经存在的线程

使用info threads可以看到程序中所有线程的信息

代码语言:txt
复制
(gdb) info threads
  3 Thread 0x7ffff688a700 (LWP 20568)  0x00007ffff70be8e0 in sigprocmask () from /lib64/libc.so.6
  2 Thread 0x7ffff708b700 (LWP 20567)  0x00007ffff7138a3d in nanosleep () from /lib64/libc.so.6
* 1 Thread 0x7ffff7fe5720 (LWP 20564)  main (argc=1, argv=0x7fffffffe628) at multithreads.cpp:39

主要包括gdb分配的线程id号(例如1,2,3),操作系统分配的线程id(例如20568),线程的名字以及线程相关的调用栈信息。

切换线程

thread threadno可以切换到指定的线程,threadno就是上面gdb分配的线程id号。

代码语言:txt
复制
(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff708b700 (LWP 20567))]#0  0x00007ffff7138a3d in nanosleep () from /lib64/libc.so.6

锁定一个线程

默认情况下gdb不锁定任何线程

代码语言:txt
复制
(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff708b700 (LWP 20567))]#0  threadPrintHello (arg=0x0) at multithreads.cpp:10
10              std::cout << "hello" << std::endl;
(gdb) n
helloworld

In main thread
7           while(1)
(gdb) n
9               sleep(5);
(gdb) n
world
In main thread
10              std::cout << "hello" << std::endl;

当我们切换到线程2的时候,使用next执行的时候,发现打印的结果中包含有其他线程的打印信息。

可以使用set scheduler-locking on来锁定只有当前的线程能够执行。

代码语言:txt
复制
(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff708b700 (LWP 20567))]#0  threadPrintHello (arg=0x0) at multithreads.cpp:10
10              std::cout << "hello" << std::endl;
(gdb) set scheduler-locking on
(gdb) n
hello
7           while(1)
(gdb) n
9               sleep(5);
(gdb) n
10              std::cout << "hello" << std::endl;
(gdb) n
hello
7           while(1)
(gdb) n
9               sleep(5);
(gdb) n
10              std::cout << "hello" << std::endl;

可以发现锁定线程之后,使用next执行变不会有其它线程的打印结果。

执行命令

使用thread apply来让一个或是多个线程执行指定的命令。例如让所有的线程打印调用栈信息。

代码语言:txt
复制
(gdb) thread apply all bt

Thread 3 (Thread 0x7ffff688a700 (LWP 20568)):
#0  0x00007ffff7138a3d in nanosleep () from /lib64/libc.so.6
#1  0x00007ffff71388b0 in sleep () from /lib64/libc.so.6
#2  0x000000000040091e in threadPrintWorld (arg=0x0) at multithreads.cpp:18
#3  0x00007ffff74279d1 in start_thread () from /lib64/libpthread.so.0
#4  0x00007ffff71748fd in clone () from /lib64/libc.so.6

Thread 2 (Thread 0x7ffff708b700 (LWP 20567)):
#0  threadPrintHello (arg=0x0) at multithreads.cpp:10
#1  0x00007ffff74279d1 in start_thread () from /lib64/libpthread.so.0
#2  0x00007ffff71748fd in clone () from /lib64/libc.so.6

Thread 1 (Thread 0x7ffff7fe5720 (LWP 20564)):
#0  0x00007ffff7138a3d in nanosleep () from /lib64/libc.so.6
#1  0x00007ffff71388b0 in sleep () from /lib64/libc.so.6
#2  0x00000000004009ea in main (argc=1, argv=0x7fffffffe628) at multithreads.cpp:47

gdb多进程调试

gdb进行多进程调试主要有以下几种方法,分别是follow-fork-mode 方法,attach 子进程方法。

示例程序

该示例程序中子进程会有除0异常

代码语言:txt
复制
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int wib(int no1, int no2)
{
    int result, diff;
    diff = no1 - no2;
    result = no1 / diff;
    return result;
}

int main()
{
    pid_t pid;

    pid = fork();
    if (pid < 0)
    {
        printf("fork err\n");
        exit(-1);
    }
    else if (pid == 0)
    {
        /* in child process */
        sleep(6);
        int value = 10;
        int div = 6;
        int total = 0;
        int i = 0;
        int result = 0;

        for (i = 0; i < 10; i++)
        {
            result = wib(value, div);
            total += result;
            div++;
            value--;
        }

        printf("%d wibed by %d equals %d\n", value, div, total);
        exit(0);
    }
    else
    {
        /* in parent process */
        sleep(4);
        waitpid(-1, NULL, 0);
        exit(0);
    }
}

follow-fork-mode

  1. follow-fork-mode:set follow-fork-mode parent|child
代码语言:txt
复制
parent:fork之后继续调试父进程,子进程不受影响。
代码语言:txt
复制
child:fork之后调试子进程,父进程不受影响。
代码语言:txt
复制
如果需要调试子进程,那么gdb启动之后,执行`(gdb) set follow-fork-mode child`并在子进程处设置断点。
  1. detach-on-fork:set detach-on-fork on|off
代码语言:txt
复制
on:断开调试follow-fork-mode指定的进程。
代码语言:txt
复制
off:gdb将控制父进程和子进程。follow-fork-mode指定的进程将被调试,另一个进程置于暂停(suspended)状态。
  1. inferior ID:切换到指定的inferior(gdb称任何执行中的进程为inferior)
  2. info inferiors:列出当前被gdb调试的每个inferior信息
  3. detach inferior ID:detach指定的inferior,允许其正常运行

follow-fork-mode调试多进程

设置断点,并且设置detach-on-fork为关闭来终止fork之后子进程的运行(默认情况下follow-fork-mode都是从父进程开始)

代码语言:txt
复制
(gdb) b 18                 
Breakpoint 1 at 0x400740: file multiprocess.cpp, line 18.
(gdb) set detach-on-fork off
(gdb) run
Starting program: /data/home/chainyang/small_program/multiprocess 
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
[New process 3689]
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.tl1.6.x86_64 libgcc-4.4.6-4.tl1.x86_64 libstdc++-4.4.6-4.tl1.x86_64

Breakpoint 1, main () at multiprocess.cpp:18
18          if (pid < 0)
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.tl1.6.x86_64 libgcc-4.4.6-4.tl1.x86_64 libstdc++-4.4.6-4.tl1.x86_64

当有一个新的子进程通过fork被创建出来,gdb会给出提示信息,如上面的[New process 3689]

通过info inferiors列出当前被gdb调试的进程,其中*表示当前所在的进程

代码语言:txt
复制
(gdb) info inferiors 
  Num  Description       Executable        
  2    process 3689      /data/home/chainyang/small_program/multiprocess 
* 1    process 3686      /data/home/chainyang/small_program/multiprocess 

进程相关的信息主要包括inferior ID(gdb用来标识的进程id),操作系统标识的进程id以及可执行程序的名字。

通过inferior ID切换到指定的进程运行

代码语言:txt
复制
(gdb) b 31
Breakpoint 2 at 0x40079a: file multiprocess.cpp, line 31. (2 locations)
(gdb) inferior 2
[Switching to inferior 2 [process 6251] (/data/home/chainyang/small_program/multiprocess)]
[Switching to thread 2 (process 6251)] 
#0  0x00007ffff7355b1d in fork () from /lib64/libc.so.6
(gdb) c
Continuing.

Breakpoint 1, main () at multiprocess.cpp:18
warning: Source file is more recent than executable.
18          if (pid < 0)
(gdb) c
Continuing.

Breakpoint 2, main () at multiprocess.cpp:31
31              int result = 0;

示例程序在子进程中有算数异常(除0)

代码语言:txt
复制
Program received signal SIGFPE, Arithmetic exception.
0x0000000000400725 in wib (no1=8, no2=8) at multiprocess.cpp:9
9           result = no1 / diff;

通过detach inferior ID来detach指定的进程,让它自由执行完

代码语言:txt
复制
(gdb) detach inferior 2
Detaching from program: /data/home/chainyang/small_program/multiprocess, process 6948
(gdb) info inferiors 
  Num  Description       Executable        
* 2    <null>            /data/home/chainyang/small_program/multiprocess 
  1    process 6945      /data/home/chainyang/small_program/multiprocess 

被detach的进程会显示null

此时切换到主进程中继续运行,由于子进程异常终止,父进程收到异常信号之后就退出程序。

代码语言:txt
复制
(gdb) inferior 1
[Switching to inferior 1 [process 6945] (/data/home/chainyang/small_program/multiprocess)]
[Switching to thread 1 (process 6945)] 
#0  main () at multiprocess.cpp:18
18          if (pid < 0)
(gdb) c
Continuing.

Program exited normally.
Segmentation fault

attach进程

gdb可以通过attach对正在执行的程序进行调度,它允许开发人员中断程序 并查看其状态,之后还能让这个程序正常地继续执行。

attach调试子进程

更改上述示例程序子进程睡眠时间为60秒,然后让子进程在后台运行,然后通过ps命令查询到子进程ID

代码语言:txt
复制
[chainyang@DSNO_DP_PD_2 ~/small_program]$ ./multiprocess &
[1] 9448
[chainyang@DSNO_DP_PD_2 ~/small_program]$ ps aux | grep multiprocess
511       9448  0.0  0.0  11788   720 pts/4    S    11:38   0:00 ./multiprocess
511       9449  0.0  0.0  11788   136 pts/4    S    11:38   0:00 ./multiprocess
511       9451  0.0  0.0   6428   592 pts/4    S+   11:38   0:00 grep multiprocess

这里子进程id为9449

然后gdb attach到该子进程,通过stop中止当前的进程,并且设置断点

代码语言:txt
复制
<http://www.gnu.org/software/gdb/bugs/>.
(gdb) attach 9449
Attaching to process 9449
Reading symbols from /data/home/chainyang/small_program/multiprocess...done.
Reading symbols from /usr/lib64/libstdc++.so.6...(no debugging symbols found)...done.
Loaded symbols for /usr/lib64/libstdc++.so.6
Reading symbols from /lib64/libm.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libm.so.6
Reading symbols from /lib64/libgcc_s.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libgcc_s.so.1
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffd145be000
0x00007fb9d69f7a20 in __nanosleep_nocancel () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.tl1.6.x86_64 libgcc-4.4.6-4.tl1.x86_64 libstdc++-4.4.6-4.tl1.x86_64
(gdb) stop
(gdb) b 37
Breakpoint 1 at 0x4007b2: file multiprocess.cpp, line 37.

然后通过断点调试找到除0异常

代码语言:txt
复制
(gdb) c
Continuing.

Program received signal SIGFPE, Arithmetic exception.
0x0000000000400725 in wib (no1=8, no2=8) at multiprocess.cpp:9
9           result = no1 / diff;
(gdb) p diff
$2 = 0

参考

http://blog.csdn.net/lhl_blog/article/details/8888010

https://www.ibm.com/developerworks/cn/linux/l-cn-gdbmp/

http://blog.csdn.net/fansongy/article/details/6771309

https://sourceware.org/gdb/onlinedocs/gdb/Forks.html

http://moss.cs.iit.edu/cs351/gdb-inferiors.html

https://wizardforcel.gitbooks.io/100-gdb-tips/content/attach-process.html

下一篇
举报
领券