首页
论坛
课程
招聘
[原创]linux gdb调试(工作中总结)
2022-4-13 19:12 8890

[原创]linux gdb调试(工作中总结)

2022-4-13 19:12
8890

一、gdb常用调试命令一览表


功能

命令

简写命令

例子

备注

调试程序app,并传递参数

gdb app

set args 10



显示传递给main函数的参数

show args



显示源代码,默认10行

list

l



设置断点

①break 文件名:行号

②break 文件名:函数名

③break 行号

③break 函数名

b

例1行号下断点

b main.cpp:6

例2 函数名下断点

b   point.cpp:point::print


设置条件断点

break [break-args] if (condition)


①b main if argc > 1

②b 180 if   (string == NULL && i < 0)

③b test.c:34 if   (x & y) == 1

④b myfunc if i %   (j + 3) != 0

⑤b 44 if   strlen(mystring) == 0


设置指定线程的断点

①break <linespec> thread <threadno>

②break   <linespec> thread <threadno> if ...


①break frik.c:13 thread 28 if bartab >   lim

linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID,注意,这个ID是GDB分配的,你可以通过"info   threads"命令来查看

设置内存断点

①watch + [变量][表达式]  当变量或表达式值改变时即停住程序。
  ②rwatch + [
变量][表达式] 当变量或表达式被读时,停住程序。
  ③awatch + [
变量][表达式] 当变量或表达式被读或被写时,停住程序。


①watch *(int*)0x22cbc0


运行

run

r


整个调试过程,只能运行一次run命令,第二次运行run命令,调试器会询问你是否从新开始调试。如果要运行到下一个断点,不能用run,必须使用continue或者其简写c

运行到下一个断点

continue

c


相当于VS里的F5

运行到下一个断点,输入c,不能输入r,输入run的话会重新开始调试,

单步执行,不能入函数

next

n


相当于vs的F10

单步执行,进入函数

step

s


相当于vs的F11,step/finish组合

跳出函数

finish


相当于vs的Shfit +   F11,函数完整执行后返回,step/finish组合,注意不能使用简写f。f是frame的缩写

跳过当前函数后面的语句直接返回,返回值可以自定义

return 

return 119

step/return组合

查看函数调用堆栈

backtrace

bt



查看设置的断点

info break

i b


注意i和b之间有个空格

查看寄存器的值

info register

i r


注意i和r之间有个空格

查看栈帧信息

①info frame

②info frame 栈帧编号

i f


注意i和f之间有个空格

查看当前stack frame局部变量

info locals

i local


注意不能缩写成i l

删除断点

delete [breakpoints num] [range...]

d

例1:删除序号为5的断点

d 5

例2:删除序号1到4的断点

d 1-4


删除断点(删除指定行的断点,作用范围为1行

①clear function

②clear   filename:function

③clear   line_number

④clear filename:line_number



禁用断点

disable 断点编号




启用断点

enable 断点编号




打印单个变量值

①print /format 变量名

/x 按十六进制格式显示变量。
  /d
按十进制格式显示变量。
  /u
按十六进制格式显示无符号整型。
  /o
按八进制格式显示变量。
  /t
按二进制格式显示变量。
  /a
按十六进制格式显示变量。
  /c
按字符格式显示变量。
  /f
按浮点数格式显示变量。

p

例1十进制打印nNum:

(gdb) p nNum
  $2 = 100

例2十六进制打印nNum:

(gdb) p /x nNum
  $2 = 0x64

例3计算常量表达式的值:

(gdb) p /x   0x22cb70+0x50


打印数组

p 数组第一个元素@打印长度

p

例1打印int数组的前10个元素:

(gdb) p   nScore[0]@10

例2打印int数组的前10个元素:

(gdb) p   *nScore@10

例3以16进制打印数组前10个元素。

(gdb) p /x   *nScore@10


打印内存

打印内存:x  /nfu   addr (以f格式打印n个u类型存储单元的以addr开头的内存值)

格式f: o(octal)  x(hex) d(decimal) u(unsigned decimal)   t(binary) f(float) a(address) i(instruciton) c(char) s(string)

字节u: b(byte)  h(halfword)  w(word)    g(gaint, 8 bytes)


例1:以二进制显示addr处开始的256个字节

x    /256xb addr

例2:以十六进制显示addr处开始的256个字节

x    /256xw addr


修改变量的值

set variable key = value
  set var key = value


例1 :修改char数组

set var   chArr="hello lily"

例2:修改int变量

set var nNum=102

例3:修改int数组的某个元素

set var nScore[2]=998


修改并查看变量的值

print 变量名=值


例1:修改并打印char数组

p   chArr="sal"

例2:修改并打印int变量值

p nNum=666

例3:修改并打印int数组的某个元素值

p nScore[0]=666


查看线程信息,并切换线程

info thread

thread 线程编号

i  thread

t 线程编号



显示调用堆栈并切换栈帧

bt

frame 栈帧编号

f

例1切换到栈帧1

f 1

首先使用bt命令显示调用堆栈,

再使用f命令切换栈帧,切换栈帧仅仅是因为gdb没有ui界面,相当于点击vs调用堆栈的某个栈帧。没有发生线程切换

再使用info local查看函数的局部变量。

清屏

!clear




结束调试

quit

q





二、常见调试场景

以下调试源代码:

                                              

2.1普通调试

(1)开始调试,并设置传递给main函数的参数

命令:gdb 要调试的程序名

set args 参数1  参数2 ......

2)设置、查看断点,并运行命中断点。

  

3)查看并修改变量的值

4)显示指定内存地址的内容


5)单步执行命令n(不进入函数)和s(进入子函数)

6)下断点,并查看断点列表,运行命中断点,切换栈帧并查看局部变量


(7)删除断点

2.2附加调试

1)查看要调试的程序pid

命令:ps -aux | grep attachDemo


2)附加进程调试

执行gdb attach pid即可调试正在运行的程序,

执行:$ gdb attach 29231,若执行gdb attach时提示:” ptrace: Operation not permitted”,则执行:$ sudo gdb attach 29231

3)在工作者线程中下断点

4)运行命中断点

2.3多线程死锁调试

1)编写以下死锁代码,文件名字deadLock.cpp

// deadLock.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
 
#include <thread>
#include <iostream>
#include <mutex>
using namespace std;
mutex _mutex1;
mutex _mutex2;
int date1;
int date2;
 
int do_work_1()
{
    cout << "thread_1 start" << endl;
    lock_guard<mutex> locker1(_mutex1);
    date1++;
    this_thread::sleep_for(chrono::seconds(1));
     lock_guard<mutex> locker2(_mutex2);
    date2++;
    cout << "thread_1 end" << endl;
   return 0;
}
 
 
int do_work_2()
{
    cout << "thread_2 start" << endl;
    lock_guard<mutex> locker2(_mutex2);
    date2++;
    this_thread::sleep_for(chrono::seconds(1));
    lock_guard<mutex> locker1(_mutex1);
     date1++;
     cout << "thread_2 end" << endl;
    return 0;
}
 
int main()
{
    thread t1(do_work_1);
    thread t2(do_work_2);
    t1.join();
    t2.join();
    cout << "end" << endl;
    return 0;
}

2)编译文件

g++ deadLock.cpp -g -o deadlock -std=c++11 -pthread

3)调试

gdb deadlock

4)执行r,产生死锁

5 按下ctrl + C 使程序中断。

查看当前主线程的栈帧(bt)

查看栈帧3停的位置

初步判断是产生了死锁,因为42行的join没有执行。

查看线程信息,命令info thread,简写i thread*号打头的为当前线程。

查看一下当前的线程,切换到2号线程,切换线程指令thread tid t tid)并查看对应的栈帧。死锁在19

查看锁的拥有者命令:p mutex名字

切换到3#线程,切换线程指令thread tid t tid)并查看对应的栈帧。死锁在32

2.4 coredump调试

1 设置core dump文件生成的方法

a)在终端中输入ulimit -c 如果结果为0,说明当程序崩溃时,系统并不能生成core dump
b
使用ulimit -c unlimited命令,开启core dump功能,使用ulimit -c filesize命令,可以限制core文件的大小(filesize的单位为kbyte)。若ulimit -c unlimited,则表示core文件的大小不受 限制。如果生成的信息超过此大小,将会被裁剪,最终生成一个不完整的core文件。在调试此core文件的时候,gdb会提示错误。

c/proc/sys/kernel/core_uses_pid可以控制core文件的文件名中是否添加pid作为扩展。文件内容为1,表示添加pid作为扩展名,生成的core文件格式为core.xxxx;为0则表示生成的core文件同一命名为core

2)编写以下死锁代码,文件名字main.cpp

// main.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
 
#include <iostream>
using namespace std;
 
int Div(int a, int b)
{
    int nDiv = a / b;
    return nDiv;
}
 
int main()
{
    int a = 100;
    int b = 0;
    int nDiv = 0;
 
    std::cout << "enter main" << std::endl;
    nDiv = Div(a, b);
    std::cout << "nDiv=" << nDiv << std::endl;
}

3)运行程序,生成dump

4)调试core dump文件,定位崩溃点。

agdb 程序名  coredump文件名

b)输入bt命令,查看崩溃的调用堆栈

2.5内存断点

1)编写以下测试代码

#include <stdio.h>
#include <string.h>
 
int main()
{
    int i, j;
    char buf[256] = { 0 };
    char* pp = buf;
 
    printf("buf addr= 0x%x\n", buf);
 
    for (i = 0; i < 16; i++)
    {
        printf("addr = 0x%x~0x%x\n", pp + i * 16, pp + i * 16 + 15);
        for (j = 0; j < 16; j++)
            *(pp + i * 16 + j) = i * 16 + j;
    }
    printf("ASCII table:\n");
 
    for (i = 0; i < 16; i++)
    {
      for (j = 0; j < 16; j++)
         printf("%c  ", *(pp + i * 16 + j));
       printf("\n");
    }
    return 0;
}

2)调试程序,在第11行下断点(buf数组初始化为0),命中11行断点。

3)查看下内存断点之前的变量buf的值,然后在buf的第80个元素buf[80]处下内存断点,并运行。



看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

最后于 2022-4-13 19:14 被sanganlei编辑 ,原因:
收藏
点赞2
打赏
分享
最新回复 (2)
雪    币: 761
活跃值: 活跃值 (834)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
huangjw 活跃值 2022-4-13 21:57
2
0
verygood
雪    币: 445
活跃值: 活跃值 (1335)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kakasasa 活跃值 2022-4-13 23:42
3
0
感谢分享,mark
游客
登录 | 注册 方可回帖
返回