• Linux学习——Gdb基本调试方法&&多线程调试


    1.Gdb的基本调试

    示例代码

    //e.c
     #include <stdio.h>
     void debug(char *str)
    {
        printf("debug info :%s
    ",str );
    }
    
    int main(int argc,char *argv[]){
        int i,j;
        j=0;
        for(i=0;i<10;i++){
            j+=5;
            printf("now a=%d
    ", j);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    gcc -g -o e e.c
    调试gdb e
    或者输入gdb
    然后 file e

    1. list 命令用法

    list命令显示多行源代码,从上次的位置开始显示,默认情况下,一次显示10行,第一次使用时,从代码其实位置显示。

    list n显示已第n行未中心的10行代码
    list functionname显示以functionname的函数为中心的10行代码
    
    • 1
    • 2

    2. 断点命令break
    break location:在location位置设置断点,改位置可以为某一行,某函数名或者其它结构的地址。gdb会在执行该位置的代码之前停下来.

    使用delete breakpoints 断点号 删除断点
    这里的断点号表示的是第几个断点,刚才执行break 10返回 
    reakpoint 1 at 0x40050a: file e.c, line 10.
    中的1表示该断点的标号,因此使用 delete breakpoints 1表示删除第10行所定义的断点
    
    clear n表示清除第n行的断点,因此clear 10等同于delete breakpoints 1
    disable/enable n表示使得编号为n的断点暂时失效或有效
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可使用info查看断点相关的信息
    info breakpoints

    c(continue),继续程序运行直到下一个断点

    3.display命令
    查看参数的值

    4.step及next命令
    step可使得程序逐条执行,即执行完一条语句然后在下一跳语句前停下来,等待用户的命令。一般使用step命令是,可使用display或者watch命令查看变量的变化,从而判断程序行为是否符合要求。当下一条指令为函数时,s进入函数内部,在其第一条语句前停下来。next单步执行,但不进入函数内部。
    step n,next n 表示连续但不执行n条指令,如果期间遇到断点,则停下来
    5.print打印内部变量值
    6.bt 查看堆栈信息
    7.watch 监视变量值的变化

    watch通常需要和break,run,continue联合使用。

    下面举例说明:

    代码如下:
    #include <stdio.h>

    int main()
    {
    int a=0;
    for(int i=0; i<10; i++)
    a+=i;
    }
    调试的时候过程如下:

    (gdb) l
    1 #include <stdio.h>
    2
    3 int main()
    4 {
    5 int a=0;
    6 for(int i=0; i<10; i++)
    7 a+=i;
    8 }
    (gdb) b 5 -------在第5行设置断电
    Breakpoint 1 at 0x80483ba: file a.cpp, line 5.
    (gdb) r -------执行到断点处停止
    Starting program: /a.o

    Breakpoint 1, main () at a.cpp:5
    5 int a=0;
    (gdb) watch a -------观察a的值,当有变化时,停止
    Hardware watchpoint 2: a
    (gdb) c -------继续执行,当a的值变化时停止
    Continuing.
    Hardware watchpoint 2: a

    Old value = 0
    New value = 1
    main () at a.cpp:6
    6 for(int i=0; i<10; i++)
    (gdb)
    Continuing.
    Hardware watchpoint 2: a

    Old value = 1
    New value = 3
    main () at a.cpp:6
    6 for(int i=0; i<10; i++)
    (gdb)
    Continuing.
    Hardware watchpoint 2: a

    即,在使用watch时步骤如下:

    1. 使用break在要观察的变量所在处设置断点;

    2. 使用run执行,直到断点;

    3. 使用watch设置观察点;

    4. 使用continue观察设置的观察点是否有变化。

    8.set variable value=x 动态改变变量值
    1,调试中需要修改临时变量的值时,可以使用set命令
    语法:

    set variable key = value
    set var key = value
    示例:
    (gdb) set variable array[1] = 12

    2,另一种更简单的方式,使用print命令修改
    语法:
    print key=value
    在这里插入图片描述

    2.多线程调试

    1. 线程的查看
    首先创建两个线程:

    #include <stdio.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <stdlib.h>
    #include <string.h>
    
    void* pthread_run1(void* arg)
    {
        (void)arg;
    
        while(1)
        {
            printf("I am thread1,ID: %d
    ",pthread_self());
            sleep(1);
        }
    }
    
    void* pthread_run2(void* arg)
    {
        (void)arg;
    
        while(1)
        {
            printf("I am thread2,ID: %d
    ",pthread_self());
            sleep(1);
        }
    }
    
    
    int main()
    {
    
        pthread_t tid1;
        pthread_t tid2;
    
        pthread_create(&tid1,NULL,pthread_run1,NULL);
        pthread_create(&tid2,NULL,pthread_run2,NULL);
    
        printf("I am main thread
    ");
    
        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    分析:上面程序中创建了两个线程,程序执行起来,main函数所在程序为主线程,在这个主线程中有两个新线程运行。
    命令行查看:

    //查看当前运行的进程
    ps aux|grep a.out
    //查看当前运行的轻量级进程
    ps -aL|grep a.out
    //查看主线程和新线程的关系
    pstree -p 主线程id
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述
    2. 线程栈结构的查看

    1. 获取线程ID
    2. 通过命令查看栈结构 ps stack 线程ID
      在这里插入图片描述
      3. 利用gdb查看线程信息
      将进程附加到gdb调试器当中,查看是否创建了新线程:gdb attach 主线程ID
      在这里插入图片描述

    查看线程的一些信息

    //1.查看进程:info inferiors
    //2.查看线程:info threads
    //3.查看线程栈结构:bt
    //4.切换线程:thread n(n代表第几个线程)
    在这里插入图片描述

    4. 利用gdb调试多线程
      当程序没有启动,线程还没有执行,此时利用gdb调试多线程和调试普通程序一样,通过设置断点,运行,查看信息等等,在这里不在演示,最后会加上调试线程的命令

    设置断点

    //1. 设置断点:break 行号/函数名
    //2. 查看断点:info b
    
    • 1
    • 2

    在这里插入图片描述
    执行线程2的函数,指行完毕继续运行到断点处

    1. 继续使某一线程运行:thread apply 1-n(第几个线程) n
    2. 重新启动程序运行到断点处:r
    
    • 1
    • 2

    在这里插入图片描述
    3.只运行当前线程

    . 设置:set scheduler-locking on 2
    . 运行:n
    
    • 1
    • 2

    在这里插入图片描述
    4.所有线程并发执行

        1. 设置:set scheduler-locking off
        2. 运行:n
    
    • 1
    • 2

    在这里插入图片描述

    注意点:
    ctrl+c ctrl+d ctrl+z 的区别和使用场景

    Ctrl+C :强制中断程序,程序无论运行哪里都停止。

    Ctrl+D :发送一个 exit 的信号,退出当前的用户或者是客户端。

    Ctrl+Z :暂停程序,在进程中维持挂起状态。

    引用别人的说法:

    1、Ctrl+C比较暴力,就是发送Terminal到当前的程序,比如你正在运行一个查找功能,文件正在查找中,Ctrl+C就会强制结束当前的这个进程。
    2、Ctrl+Z 是把当前的程序挂起,暂停执行这个程序,比如你正在mysql终端中,需要出来搞点其他的文件操作,又不想退出mysql终端(因为下次还得输入用户名密码进入,挺麻烦),于是可以ctrl+z将mysql挂起,然后进行其他操作,然后输入 fg 回车后就可以回来,当然可以挂起好多进程到后台,然后 fg 加编号就能把挂起的进程返回到前台。当然,配合bg(后台)和fg命令进行前后台切换会非常方便。
    3、Ctrl+D 是发送一个exit信号,没有那么强烈,类似ctrl+C的操作,比如你从管理员root退回到你的普通用户就可以这么用。

  • 相关阅读:
    谷粒商城学习——P41vue组件化
    谷粒商城学习——P40计算属性、侦听器、过滤器
    谷粒商城学习——P38-39Vue-指令-单向绑定&双向绑定&v-onv-forv-if
    谷粒商城学习——P37vue基本语法——双向绑定、事件处理
    谷粒商城学习——P36Vue介绍与HelloWord
    谷粒商城学习——P35模块化
    谷粒商城学习——P34Promise异步编排
    Windows tcp/ip(CVE-2020-16898)远程代码执行蓝屏漏洞复现
    QQ群关系可视化3D查询搭建
    Skywalking 8.1 Docker 服务端部署
  • 原文地址:https://www.cnblogs.com/jack-hzm/p/11372285.html
Copyright © 2020-2023  润新知