【Linux】gdb调试core文件

编写服务器端程序,很容易遇到Crash问题,比较幸运的是Linux提供了core file,保留了Crash的现场。有时候,根据当前的调用栈,并且打印出当前栈的变量就可以分析出crash的原因,但是,有时候看到调用栈却束手无策。下面就介绍自己通过GDB的几个命令的结合,发现一个crash的原因的过程。

下面让我们一起进入现场,来逐步发现其中的原因。

首先,还是运行gdb 命令,gdb wbxgs core.5797,来看看现场。

[[email protected] bin]# gdb wbxgs_crash core.5797  

GNU gdb Red Hat Linux (6.3.0.0-1.132.EL4rh)

……

#0 0x00000038e8d70540 in strlen () from /lib64/tls/libc.so.6

(gdb) bt

#0 0x00000038e8d70540 in strlen () from /lib64/tls/libc.so.6

#1 0x000000000057cfc0 in T120_Trace::Text_Formator::advance (this=0x7e800a70, lpsz=0x1 <Address 0x1 out of bounds>)

at ./t120trace.cpp:1464

#2 0x000000000057ceb1 in T120_Trace::Text_Formator::operator<< (this=0x7e800a70, lpsz=0x1 <Address 0x1 out of bounds>)

at ./t120trace.cpp:1411

#3 0x0000000000407927 in ~func_tracer (this=0x7e804bd0) at ../h/t120trace.h:381

#4 0x00000000004432fd in CGSSocketServer::readHeader (this=0x8e4130, socketfd=1088,

buf=0x7e806cc0 "GET /detectService?cmd=selfcheck HTTP/1.1/r/nConnection: Close/r/nHost: 10.224.122.94/r/n/r/n", bufsize=1024)

at mgr/gssocketserver.cpp:337

#5 0x0000000000443981 in CGSSocketServer::handle (this=0x8e4130, socketfd=1088, [email protected]) at mgr/gssocketserver.cpp:424

#6 0x0000000000442f5e in CGSSocketServer::readThread (pArg=0x9ae9c0) at mgr/gssocketserver.cpp:304

#7 0x00000038e980610a in start_thread () from /lib64/tls/libpthread.so.0

#8 0x00000038e8dc68b3 in clone () from /lib64/tls/libc.so.6

#9 0x0000000000000000 in ?? ()

通过这个调用栈,可以看出,程序crash在打log的时候。虽然遇到过类似的crash,但是,当时的原因是有死循环,通过review code,没有发现死循环。但是当前的调用栈对于分析Crash的原因是一点用也没有,如果分析具体的原因呢?会不会是其他得线程出现错误导致程序Crash在这个线程呢?为了找到深一层的原因,尝试着通过GDB的一些关于线程的命令,来看看其他的线程是否有问题。于是,使用info threads,查看了一下当时线程的情况。

(gdb) info threads  

21 process 5797 0x00000038e8d7186d in memset () from /lib64/tls/libc.so.6

20 process 5839 0x00000038e8dc6c8c in epoll_wait () from /lib64/tls/libc.so.6

19 process 5842 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6

18 process 5845 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6

17 process 5846 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

16 process 5847 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

15 process 5848 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

14 process 5849 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

13 process 5850 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

12 process 5852 0x00000038e8dbf946 in __select_nocancel () from /lib64/tls/libc.so.6

11 process 5854 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

10 process 5856 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

9 process 5857 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

8 process 5858 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

7 process 5859 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6

6 process 5861 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

5 process 5862 0x00000038e980a66f in sem_wait () from /lib64/tls/libpthread.so.0

4 process 5863 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6

3 process 5864 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6

2 process 5883 0x00000038e8d8f7d5 in __nanosleep_nocancel () from /lib64/tls/libc.so.6

* 1 process 5853 0x00000038e8d70540 in strlen () from /lib64/tls/libc.so.6

对于线程如果停止在sleep或者wait的情况,都是正常的,但是我们看到thread 21有些异常,程序停止在memset,不管是否有问题,都需要看看这样的线程具体有没有出错。

于是通过命令thread 21,进入到thread 21的调用栈。

(gdb) thread 21  

[Switching to thread 21 (process 5797)]#0 0x00000038e8d7186d in memset () from /lib64/tls/libc.so.6

(gdb) bt  

#0 0x00000038e8d7186d in memset () from /lib64/tls/libc.so.6

#1 0x000000000049da0d in CGSPduFactory::streamStringFrom ([email protected], [email protected]) at common/pdu/gspdu.cpp:422

#2 0x00000000004d1f25 in CGSOthShardUserRspPdu::streamFrom (this=0x2aaaec951650, [email protected]) at common/pdu/pdugs.cpp:2707

#3 0x000000000049cb2d in CGSPduFactory::derivePdu ([email protected], ulPDULen=30506) at common/pdu/gspdu.cpp:79

#4 0x000000000049c78e in CGSPduFactory::streamPduFrom (pDataPacket=0x2aaaeca31d70) at common/pdu/gspdu.cpp:35

#5 0x0000000000449681 in CGSWDMSManager::on_wdms_message_indication (this=0x8e3680, msg=0x2aaae9894360)

at mgr/gswdmsmanager.cpp:344

……

#18 0x0000000000407733 in main (argc=1, argv=0x7fff9b44ac98) at gsmain.cpp:118

(gdb) f 3  

#3 0x000000000049cb2d in CGSPduFactory::derivePdu ([email protected], ulPDULen=30506) at common/pdu/gspdu.cpp:79

79      common/pdu/gspdu.cpp: No such file or directory.

in common/pdu/gspdu.cpp

使用命令 i locals,打印所有的变量的值。

(gdb) i locals  

pPdu = (CBasePdu *) 0x2aaaec951650

pPduHeader = (CPduHeader *) 0x2aaaea1c4190

ulPduType = 50

到现在还没有看出有什么明显的异常,然后再把PDU的头打印出来如下:

(gdb) p *pPduHeader

$1 = {m_ulHeadLen = 61, m_ulVersion = 2080000, m_ulPduType = 50, m_ulSrcSvrType = WEBEX_CONNECT_GS, m_strSrcSvrAddr = {

static npos = 18446744073709551615,

_M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>},

_M_p = 0x2aaaeca52a68 "10.224.95.109:9900"}}, m_strSubject = {static npos = 18446744073709551615,

_M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>},

_M_p = 0x2aaaec929b28 "qawin.qazone.GS"}}, m_ulSequence = 0}

从蓝色的字的部分可以看出,这个PDU是从10.224.95.109这台server上发过来的。

当时QA测试的环境,都是10.224.122开头的IP的server,怎么会有这个IP的PDU,于是,询问QA,发现10.224.95.109这个server是其他DataCenter的Server,而且还是老的版本,由于当前测试环境的版本删除了两个PDU,同时又增加了四个PDU,导致了老的PDU发来的时候,新的版本的把它当作新的PUD解析,从而导致不能正确解析,最终导致了解析出来的长度不对。可以通过f 1命令进入第一级调用栈查看所有的局部变量。

(gdb) f 1

#1 0x000000000049da0d in CGSPduFactory::streamStringFrom ([email protected], [email protected]) at common/pdu/gspdu.cpp:422

422                  in common/pdu/gspdu.cpp

(gdb) i locals  

strTmp = 0x2aaaf1c00010 ""

iRet = 0

ulLen = 1179995975  

可以看出解析出来的长度是一个很大的值1179995975,而线程21正式停止在分配内存之后,使用memset时,停止在那里。从Log中也可以看到,thread 21也一致阻塞在这里,而且没有再继续运行。

由于当时有两台server crash,通过查看另外一台server的core file,发现另外一台server也是和本台server一样的调用栈。在QA更新了10.224.95.109的版本后,crash没有再出现。

通过这个实例,可以看出,当server出现crash的时候,虽然当前的调用栈可能没有什么价值,但是,通过分析所有线程的调用栈,还是可能分析出蛛丝马迹的,从而对于解决Crash的问题带来帮助。

通过这个问题可以得到一个教训,在修改Server之间的接口时,一定要考虑到和老版本的兼容问题,即使这个PDU可能永远也不会使用,仍然需要保留,因为Production上,是先上GSB,然后再上Primary,肯定会存在两个版本同时运行的情况。如果出现删除或者改变PDU顺序的情况,可能会导致整个系统不能工作。

希望本文章,对解决Crash问题和避免类似的Crash问题有一定的借鉴作用。

时间: 2024-10-24 18:42:59

【Linux】gdb调试core文件的相关文章

Linux下交叉编译gdb,gdbserver+gdb的使用以及通过gdb调试core文件

交叉编译gdb和gdbserver 1.下载gdb:下载地址为:http://ftp.gnu.org/gnu/gdb/按照一般的想法,最新版本越好,因此下载7.2这个版本.当然,凡事无绝对.我们以gdb-7.2.tar.bz2 这个文件为例.2.解压缩: $ tar jxvf gdb-7.2.tar.bz2 注:小技巧:Linux下一般压缩文件后缀为.tar.bz2和.tar.gz,它们解压命令有两三个选项是一致的: xf(v),前者再加上j选项,后者再加上z选项. 3.进入该目录 $ cd g

gdb调试core文件

什么是Core Dump?Core的意思是内存, Dump的意思是扔出来, 堆出来.开发和使用Unix程序时, 有时程序莫名其妙的down了, 却没有任何的提示(有时候会提示core dumped). 这时候可以查看一下有没有形如core.进程号的文件生成, 这个文件便是操作系统把程序down掉时的内存内容扔出来生成的, 它可以做为调试程序的参考.core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core

解决gdb 调试 core 文件函数名显示为问号的问题

关于gdb调试core文件总是一堆问号的问题 问题描述:已经在编译选项中加入了-g,但是查看core文件时,还是一堆问号,使用的命令为:gdb -c core 解决方案:由于gdb -c core这样的使用在有些系统下支持不是很好,所以推荐用如下两种方法: 1) gdb exe (gdb) core-file core 2) gdb -c core (gdb) file exe

gd调试命令,gdb调试core文件

使用 gcc -g test.c -o test.out 编译程序,只有加-g参数才支持gdb调试: 然后 gdb ./test.out 运行可执行文件,进入gdb调试模式(gdb),在括号后面的输入命令: (gdb)help:查看命令帮助 (gdb)start:单行执行 (gdb)l:查看源程序 (gdb)n:执行下一行 (gdb)s:进入被调函数里面 (gdb)bt:查看函数调用栈 (gdb)p 变量:打印变量值 (gdb)f:切换函数栈帧 (gdb)run:重新开始运行文件 (gdb)fi

用gdb调试core dump文件

gdb基本的使用方法在此就不说了. 载入core文件的命令行为: dgb exe core 例如 gdb ./testall ./core.2345 最重要的一个命令是where,这个就像windbg的命令 !analyze -v 我模拟了几个crash的情况,一个一个说. 第一个:删除两次指针导致crash的情况 源程序 char *a = new char[2]; delete []a; delete []a; 运行时 *** glibc detected *** ./testall: do

用gdb分析core文件及常见gdb命令操作示例

1.概述 在实际的软件开发项目中,程序出现问题是在所难免的.遥想本人参加工作之后首次遇到程序的情景,至今还历历在目.之前的经验告诉我,我们越是惊慌失措,问题就越是解决不了.我们要先让自己平静下来,然后再寻找解决程序问题的办法. 在Linux下做开发的朋友,想必都与core文件打过交道.当看到自己的程序运行之后出现core时,很多人都慌乱了,仿佛天快要塌下来一样.其实,我们大可不必如此,只要我们掌握了用gdb调试core文件的办法,依然可以很快定位程序问题,一举将bug消灭掉.有关Linux co

GDB调试core dump文件示例

上篇论述了三层的基本概念,作用,关系,现在展示下系统中用户登陆过程中简单应用三层结构设计思想. vb.NET的操作如下 首先建立以下windows应用程序以及类库 首先建立实体类 Public Class userInfo Private _username As String Public Property UserName As String Get Return _username End Get Set(ByVal value As String) _username = value E

Linux GDB调试全面解析

GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师完成下面4个方面的功能: 启动程序,可以按照工程师自定义的要求随心所欲的运行程序. 让被调试的程序在工程师指定的断点处停住,断点可以是条件表达式. 当程序被停住时,可以检查此时程序中所发生的事,并追索上文. 动态地改变程序的执行环境. 不管是调试Linux内核空间的驱动还是调试用户空间的应用程序,掌握gdb的用法都是必须.而且,调试内核和调试应用程序时使用的gdb命令是完全相同的,下面以代码清单22.2的应用程

[笔记]用gdb调试core dump

总是隔一段时间才写一次C++,有些东西老是用完就忘了……记一下如何用gdb来调试core dump免得到时候又忘记. 首先需要设置core file的大小,默认是0所以不设不会生成core file $ ulimit -c unlimited 然后在编译的flag里加上 -g -rdynamic 把动态静态符号表都弄过来 然后 $ make $ # 干点啥让它core dump 假设可执行文件叫test,生成的core file叫core(ubuntu 12.04是的,其他系统可能叫其他名字)