Posts Tagged ‘GDB’

November 4, 2013

  又是一个 Segmentation faltcore 在一个赋值操作, req->r->opacket = resp; 按照惯例,req, req->r 或者 req->r->opacket 指向的地址应该是非法的,但查看这些地址,却全都是合法的地址。查看汇编代码,程序 core 在指令 mov %rax, 0x40(%rdx) 处,%rdx 内容为 NULL,即 req->rNULL%rdx 的值是从 %r13 + 0x20 处取得的,而该处的值是 0x2aacf3986da0,不是 NULL
  只有一种可能:最初从 (%r13+0x20),即 req->r 取出的值(到 %rdx)是 NULL,在访问 0x40(%rdx) 之前,req->r 又被复制为非 NULL。那就是并发问题了。
  类似这种诡异的现象,可能还会遇到 assert(var != 0) 失败,但 var 却是非 0 的情况。
  遇到难以置信的 bug,就想想并发。

Tags: ,. 576 views
September 23, 2013

  is_alive 实现了一个超时时间可控的 connect。如果你现在已经明白怎么回事了,请务必留下您的大名,我可是花了两周零五分钟才看出来的。
  对,就是万恶的 select,FD_SET 可能越界,因为 fd_set 中只有 FD_SETSIZE 个 bit 来标识 fd。FD_SETSIZE 是一个宏,定义在 /usr/include/sys/select.h。查看该文件,FD_SET 并未对 fd 做参数检查,因此当 fd 大于 1024 时,FD_SET 就写了它不该写的地方了,写到谁,有没有影响,有多大影响,什么时候触发,程序会不会 core 掉,什么时候 core 掉,这些都要看造化了。比如这次是写到栈里的 return address 了,导致程序跑飞。另外一个 core dump 里面,某个类的 this 从 0x1e41500 变成了 0x11e41500,当时定位不出写越界,只好认为世界末日前宇宙射线爆发导致硬件异常,聊以自慰。
  吐个槽,Linux Kernel 就不能增加一个带超时的 connect 调用?glibc 里面,FD_SETSIZE 只用在 fd_set 里的位域定义和 FD_ZERO,FD_SET 没有做任何检查;Kernel 里面的 select 实现,申请内核态 fd_set 的时候,完全依据 fd 的大小,并没有大小的限制(如果我没有漏掉某些逻辑的话)。
  总结:
  如果出现 stack corruption,几乎一定是栈的缓冲区写越界了;基本不可能是用户态的堆内存越界,因为除了主线程,每个线程的栈空间两侧又有一个 page 的虚拟空间做 gap,这些 gap,不可读,不可写,不可执行。主线程的 stack 是按需向下生长的,在 IA64 环境下,也不可能和 mmap 区域无缝相邻。当然,跳着写堆内存,或者偏移量算错就另说了。
  这篇文章中 FD_SET 写越界只篡改了一个 bit,而且被篡改的那个 bit 可能本来就是 1,非常难重现,而且 stack 的破坏程度较轻,通过查看栈内容,可以大致知道函数的调用关系。如果是类似上文 foo() 中的 memset 越界,栈可能真的面目全非了,但这时候栈内容通常是有规律可循的。
  根据栈内容判断函数调用关系,需要知道 call 指令的语义,栈中保存的是 caller 中 call 指令的下一条指令,这条指令的前一条才是 callee。

Tags: ,. 1,193 views
  • set follow-fork-mode child,被调试进程执行 fork 时,自动 attach;
  • set scheduler-locking on,调试时,禁用线程切换,可选 on/off/step,默认 off;
  • symbol-file target.debug,添加独立的 debuginfo 文件;
  • i sharedlibrary,查看共享文件映射信息;
  • add-symbol-file libxx.debug ADDRESS,添加共享文件的 debuginfo 文件,ADDRESS 是共享文件的映射始址,由 i sharedlibrary 获得;
  • gcc test.cpp -g -g3,调试信息中保留 MACRO;
  • set logging on,GDB 的所有输入/输出都会被写入当前目录下的 gdb.txt;
  • set print pretty on,打印对象,尤其是结构体时,格式更加友好;
  • p $rip,打印 rip 寄存器;
  • i reg,查看寄存器集,i registers-all 显示全部寄存器;
  • display/i $rip,每次断点,打印下一条指令;
  • l *0x608048,显示某指令地址对应的代码行,可执行文件包含调试信息时,亦可用 addr2line;
  • x/40a $rsp,以地址形式打印 stack,栈乱掉时可救命;
  • return 0,停止调试当前函数,并以指定值返回;
  • p {tair::StorageManager}0x608048,将指定地址以某类型打印;
  • p *array@10,打印数组 array 的前十个元素;
  • gcore,将被调试进程 core dump,gcore 还是一个独立的命令,随 GDB 发布。
Tags: . 528 views
October 16, 2011

  core dump, 通常译作内存转储,core之所以译作内存,而不是核心,纯属“著名”的历史原因,因为早期的内存有一个叫磁芯(magnectic core)的东西。内存转储会在磁盘中产生一个文件,是某个进程在转储时刻的内存映像及寄存器等信息。内存转储通常发生在进程执行了有致命错误的指令时,常见的就是著了名的Segmentation fault,即段错误,而导致段错误的就是C/C++编程中经常发生的内存非法访问。内存转储操作由操作系统内核进行。当然,内存转储还可以是用户主动发起请求,内核执行转储。
  内存转储对于程序调试是至关重要的。利用内存转储文件(core files)对程序进行调试是一种异步的静态调试,所谓异步是指异常发生与调试不是在同一时刻,所谓静态指的是core files保存的只是转储时刻进程的内存状态。比如,一个长期运行的服务程序,我们不知道哪里有bug,该bug何时被触发,这种情况下,利用异常发生时的转储文件就可以在一定程度上定位出异常发生的原因。另外,对于非致命性错误,比如死锁或者死循环,进程不会自动结束,通常内核也不会主动杀掉这种进程,这时用户就可以请求内核将该进程进行内存转储,以此来查看进程的运行状态(比如各进程的堆栈)。

Tags: . 1,246 views
February 14, 2011

  调试内存转储文件。内存转储文件(core dump)是程序发生严重错误时操作系统产生的文件,它包含了程序崩溃时占用的内存页面的拷贝,因此使用core文件在一定程度上可以再现崩溃前夕程序的状态。
  多种原因下程序会崩溃,从而被系统终止,生成core文件,经常见到的是访存错误(段错误,Segment fault)。另外,系统是否生成core文件以及core文件的最大尺寸还受系统参数的控制。例如ubuntu下面普通用户程序是不允许生成core文件的,这可以使用ulimit命令来修改。不带参数的ulimit -c输出当前core文件的最大允许值,为了能够生成core文件,使用ulimit -c size设置大小,简单地,ulimit -c unlimited将其设为没有限制。

1
2
3
4
5
6
7
8
9
10
void foo(const char* s)
{
    char c = *s;
}
int
main()
{
    foo(0);
    return 0;
}
Tags: . 1,278 views

  列位安好。简单总结下GDB调试器的使用。

准备

  默认情况下,gcc/g++编译的可执行文件是不包含调试信息的,GDB是一个源代码级的调试器,使用GDB调试程序需要程序的源代码、符号及其对应的行号等,其中符号和行号可以是单独的文件,亦可以在编译时嵌入到可执行文件中。使用gcc/g++时使用-g选项即可将必要的调试信息包含到可执行文件中,使用-g3选项还可以将源代码中的宏信息也包含进去。
另外,调试过程中需要随时查看源代码,但源代码并没有包含到可执行文件中。通常GDB在当前目录查找源文件,但某些情况下(比如调试系统命令)需要手动指明源代码的查找目录,directory ~向GDB指明到$HOME下查找源文件。

启动

  GDB的启动很灵活,它的各种特性,你可以在Shell下通过选项和参数指定,也可以在GDB启动之后在GDB自己的命令行下使用GDB内置的命令来指定。最常用的是直接使用命令gdb PROGRAM启动,这样gdb自动加载符号表等调试信息。若要向被调试程序传递参数,可以采用gdb –args program ARG1 ARG2的形式,其中–args(或者-args)是必须的,它告诉GDB该选项之后已经没有GDB需要的选项了。另外,还可以直接使用gdb启动,然后使用file program加载调试信息。此时若要设置被调试程序的参数,可以使用set命令的args子命令,如set args ARG1 ARG2. 还有一种传递参数的方法,在下面介绍。

Tags: . 5,943 views
March 29, 2010
GDB调试命令
命令 描述
backtrace(或bt) 查看各级函数调用及参数
finish 连续运行到当前函数返回为止,然后停下来等待命令
frame(或f) 帧编号 选择栈帧
info(或i) locals 查看当前栈帧局部变量的值
list(或l) 列出源代码,接着上次的位置往下列,每次列10行
list 行号 列出从第几行开始的10行源代码
list 函数名 列出某个函数的源代码
next(或n) 执行下一行语句
print(或p) 打印表达式的值,通过表达式可以修改变量的值或者调用函数
quit(或q) 退出gdb调试环境
set var 修改变量的值
start 开始执行程序,停在main函数第一行语句前面等待命令
step(或s) 执行下一行语句,如果有函数调用则进入到函数中
break(或b) 行号 在某一行设置断点
Tags: . 777 views
Page 1 of 11