IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> gdb与coredump调试技巧 -> 正文阅读

[系统运维]gdb与coredump调试技巧

gdb介绍

?

GDB 的全称是GNU Debuger,是linux 底下的一种免費的debug程序,没有界面,当然Linux也有带界面的比如cgdb、kdbg、ddd和insight debugger,在使用gdb调试的时候我们需要在编译程序的时候生成调试信息,比如:

gcc -Wall -g3 -o test  test.c

gcc生成调试信息的一些选项:

  • -g:该选项可以利用操作系统的“原生格式(native format)”生成调试信息。GDB 可以直接利用这个信息,其它调试器也可以使用这个调试信息

  • -ggdb:使 GCC 为 GDB 生成专用的更为丰富的调试信息,但是,此时就不能用其他的调试器来进行调试了 (如 ddx)

-g 和 -ggdb 也是分级别的

  • -g2:这是默认的级别,此时产生的调试信息包括扩展的符号表、行号、局部或外部变量信息。

  • -g3:包含级别2中的所有调试信息,以及源代码中定义的宏,以及C++中的内联函数(inline)。

  • -g1:级别1(-g1)不包含局部变量和与行号有关的调试信息,因此只能够用于回溯跟踪和堆栈转储之用。回溯跟踪指的是监视程序在运行过程中的函数调用历史,堆栈转储则是一种以原始的十六进制格式保存程序执行环境的方法,两者都是经常用到的调试手段。

一般我比较习惯使用-g3。

?

启动gdb

?

调试可执行文件:

$gdb <program>

program也就是你的执行文件,一般在当前目录下。

调试core文件(core是程序非法执行后core dump后产生的文件):

$gdb <program> <core dump file>
$gdb program core.11127

调试服务程序:

$gdb <program> <PID>
$gdb hello 11127

如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。program应该在PATH环境变量中搜索得到。

?

gdb命令

?

启动gdb后,进入到交互模式,通过以下命令完成对程序的调试;注意高频使用的命令一般都会有缩写,熟练使用这些缩写命令能提高调试的效率;

运行相关命令

  • run:简记为 r ,其作用是运行程序,当遇到断点后,程序会在断点处停止运行,等待用户输入下一步的命令,run后可以带一些命令,比如我们要调试brks程序,我们第一步是gdb brks,进入gdb后,我们输入run log.conf去运行程序。

  • continue (简写c ):继续执行,到下一个断点处(或运行结束)

  • next:(简写 n),单步跟踪程序,当遇到函数调用时,也不进入此函数体;此命令同 step 的主要区别是,step 遇到用户自定义的函数,将步进到函数中去运行,而 next 则直接调用函数,不会进入到函数体内。

  • step (简写s):单步调试如果有函数调用,则进入函数;与命令n不同,n是不进入调用的函数的。

  • until:当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体。

  • until+行号: 运行至某行,不仅仅用来跳出循环。

  • finish: 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。

  • call 函数(参数):调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)

  • quit:简记为 q ,退出gdb

设置断点

  • break n (简写b n):在第n行处设置断点 (可以带上代码路径和行号: b OAGUPDATE.cpp:578)

  • b fn1 if a>b:条件断点设置

  • break func(break缩写为b):在函数func()的入口处设置断点,如:break cb_button

  • delete 断点号n:删除第n个断点

  • disable 断点号n:暂停第n个断点

  • enable 断点号n:开启第n个断点

  • clear 行号n:清除第n行的断点

  • info b (info breakpoints) :显示当前程序的断点设置情况

  • delete breakpoints:清除所有断点:

查看源代码

  • list :简记为 l ,其作用就是列出程序的源代码,默认每次显示10行。

  • list 行号:将显示当前文件以“行号”为中心的前后10行代码,如:list 12

  • list 函数名:将显示“函数名”所在函数的源代码,如:list main

  • list :不带参数,将接着上一次 list 命令的,输出下边的内容。

打印表达式

  • print 表达式:简记为 p ,其中“表达式”可以是任何当前正在被测试程序的有效表达式,比如当前正在调试C语言的程序,那么“表达式”可以是任何C语言的有效表达式,包括数字,变量甚至是函数调用。

  • print a:将显示整数 a 的值

  • print ++a:将把 a 中的值加1,并显示出来

  • print name:将显示字符串 name 的值

  • print gdb_test(22):将以整数22作为参数调用 gdb_test() 函数

  • print gdb_test(a):将以变量 a 作为参数调用 gdb_test() 函数

  • display 表达式:在单步运行时将非常有用,使用display命令设置一个表达式后,它将在每次单步进行指令后,紧接着输出被设置的表达式及值。如: display a

  • watch 表达式:设置一个监视点,一旦被监视的“表达式”的值改变,gdb将强行终止正在被调试的程序。如: watch a

  • whatis :查询变量或函数

  • info function: 查询函数

  • 扩展info locals: 显示当前堆栈页的所有变量

查询运行信息

  • where/bt :当前运行的堆栈列表;

  • bt backtrace 显示当前调用堆栈

  • up/down 改变堆栈显示的深度

  • set args 参数:指定运行时的参数

  • show args:查看设置好的参数

  • info program: 来查看程序的是否在运行,进程号,被暂停的原因。

分割窗口

  • layout:用于分割窗口,可以一边查看代码,一边测试:

  • layout src:显示源代码窗口

  • layout asm:显示反汇编窗口

  • layout regs:显示源代码/反汇编和CPU寄存器窗口

  • layout split:显示源代码和反汇编窗口

  • Ctrl + L:刷新窗口

?

coredump是什么

?

??程序异常退出时,会产生一个core文件,该文件记录了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息还有各种函数调用堆栈信息等,我们可以理解为是程序工作当前状态存储生成的一个文件,通过工具分析这个文件,我们可以定位到程序异常退出的时候对应的堆栈调用等信息,找出问题所在并进行及时解决。

??上面说当程序运行过程中异常终止或崩溃时会发生 core dump,但还没说到什么具体的情景程序会发生异常终止或崩溃,例如我们使用 kill -9 命令杀死一个进程会发生 core dump 吗?实验证明是不能的,那么什么情况会产生呢? ??Linux 中信号是一种异步事件处理的机制,每种信号对应有其默认的操作,你可以在 这里 查看 Linux 系统提供的信号以及默认处理。默认操作主要包括忽略该信号(Ingore)、暂停进程(Stop)、终止进程(Terminate)、终止并发生core dump(core)等。如果我们信号均是采用默认操作,那么,以下列出几种信号,它们在发生时会产生 core dump,这个可以通过man 7 signal: ??当然不仅限于上面的几种信号。这就是为什么我们使用 Ctrl+z 来挂起一个进程或者 Ctrl+C 结束一个进程均不会产生 core dump,因为前者会向进程发出 SIGTSTP 信号,该信号的默认操作为暂停进程(Stop Process);后者会向进程发出SIGINT 信号,该信号默认操作为终止进程(Terminate Process)。同样上面提到的 kill -9 命令会发出 SIGKILL 命令,该命令默认为终止进程。而如果我们使用 Ctrl+\ 来终止一个进程,会向进程发出 SIGQUIT 信号,默认是会产生 core dump 的。还有其它情景会产生 core dump, 如:程序调用 abort() 函数、访存错误、非法指令等等。

?

前期设置

?

  1. 设置core文件生成的目录,其中%e表示程序文件名,%p表示进程ID,否则会在程序的当前目录生成dore文件;

echo /data/coredump/core.%e.%p >/proc/sys/kernel/core_pattern ? 
  1. 当前执行程序的用户对core目录有写权限且有足够的空间存储core文件;

  2. 生成不受限制的core文件,ulimit -c unlimited,这个只会对当前的终端有效,如果想永久有效,需要修改文件:/etc/security/limits.conf,增加一行,如下红圈所示:

?

使用gdb调试coredump文件

?

产生了 core 文件,我们该如何使用该 Core 文件进行调试呢?Linux 中可以使用 GDB 来调试 core 文件,命令如下:

gdb program core

program是程序名称,core是coredump文件。OK,我们来动手吧,动手,动手,动手,当你看见一个前凸后翘的美女时,你先去了解她,制定攻略,那接下来是不是要动手实践呢?so 动手,只看书不动手永远很难了解哪些理论,即使水平再高也需要动手解决问题,才能有更深刻的认识,其实,也就是说再漂亮的女人,也需要个男人对她动粗,你说是不是? ??比如: 那都有哪些操作命令可以调试这个coredump文件呢?

  • bt或者where查看调用栈信息 如果你要查看某一层的信息,你需要切换当前的栈,一般来说,程序停止时,最顶层的栈就是当前栈,如果你要查看栈下面层的详细信息,首先要做的是切换当前栈。

  • frame <n>和 f <n>:?n是一个从0开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的第二层。

  • up <n>: 表示向栈的上面移动n层,可以不打n,表示向上移动一层。

  • down <n>: 表示向栈的下面移动n层,可以不打n,表示向下移动一层。

上面的命令,都会打印出移动到的栈层的信息。如果你不想让其打出信息。你可以使用这三个命令:

select-frame <n> 对应于 frame 命令。
up-silently <n> 对应于 up 命令。
down-silently <n> 对应于 down 命令。

查看当前栈层的信息,你可以用以下GDB命令:

  • frame 或 f ??会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。

  • info frame 和 info f ??这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时的内内地址。比如:函数地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等。

?

奔溃日志

?

生成coredump去用gdb调试还是依赖我们是否生成调试信息,如果我们的没有添加-g选项就麻烦了,此时我们可以怎么办呢? ??上文说过程序崩溃(core)了是由一些信号触发的,这些信号也许是因为用户操作,也许是我们程序有一些错误的指令(比如对空指针的赋值,除数为零)触发的。那么我们程序实际上是可以捕获这些信号的,然后去打印堆栈的信息,how to do? ??

signal是一个截取信号的函数,它的申明如下:

#include <signal.h>
?
typedef void (*sighandler_t)(int);
?
sighandler_t signal(int signum, sighandler_t handler);

signal截取信号,然后调用回调函数handler处理信号,那么其实我们就可以在handler函数里打印堆栈信息,backtrace获取堆栈的大小,并且把堆栈的信息放在array指向的内存中,然后调用backtrace_symbols把堆栈中的函数信息转换成函数名。例如:

int main(int argc, char** argv)
{
 ?  signal(SIGSEGV, handle_segv); // SIGSEGV ?  11  Core Invalid memory reference
 ?  signal(SIGABRT, handle_segv); // SIGABRT ?  6 ? ? ? Core Abort signal from
    signal(SIGINT, handle_segv);
    signal(SIGTSTP, handle_segv);
    signal(SIGTERM, handle_segv);
?
 ?  //...
    
    return 0;
}
void handle_segv(int signum)
{
 ?  void *array[100];
 ?  size_t size;
 ?  char **strings;
 ?  size_t i;
    
 ?  signal(signum, SIG_DFL); /* 还原默认的信号处理handler */
?
 ?  size = backtrace (array, 100);
 ?  strings = (char **)backtrace_symbols (array, size);
    
    // 这里是打印到日志中,其实也可以打印到某个文件中
 ?  LOG_INFO("Launcher received SIG: %d Stack trace:\n", signum);
 ?  for (i = 0; i < size; i++)
 ?  {
 ? ? ?  LOG_ERROR("%d %s \n",i,strings[i]);
 ?  }
?
 ?  free (strings);
    //g_launcher->stop();
}
从上文,我们也可以看出我们在程序崩溃前可以做一些善后处理。

最后,链接一本电子书给大家https://wizardforcel.gitbooks.io/100-gdb-tips/index.html

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-08-13 12:43:23  更:2021-08-13 12:47:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 19:11:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计