Linux下C/C++语言的调试 gdb工具 笔记

参考资料


学习地址收录

http://blog.csdn.net/tenfyguo/article/details/8159176/



1. Gdb进行调试的是可执行文件, 而不是”.c”源文件, 因此, 需要先通过Gcc编译生成可执行文件才能用Gdb进行调试.

一定要加上选项”-g,这样编译出的可执行代码中才包含调试信息, 否则Gdb无法载入该可执行文件.

在Gdb中键入l(list的缩写)可以查看所载入的文件

Linux下C/C++语言的调试 gdb工具 笔记_第1张图片

2.

在Gdb中设置断点非常简单, 只需在”b”后加入对应的行号即可(这是最常用的方式).

(gdb) b 9

注意: 该断点的作用是当程序运行到第 9 行时暂停(第 8 行执行完毕, 第 9 行未执行)

查看断点信息

(gdb) info b

接下来就可运行代码了, Gdb默认从首行开始运行代码, 可键入”r”(run的缩写)即可. 若想从程序中指定的行开始运行,可在r后面加上行号.

(gdb) r

Linux下C/C++语言的调试 gdb工具 笔记_第2张图片

3.单步运行可以使用n(next的缩写)或者s(step的缩写), 它们之间的区别在于: 若有函数调用的时候,s会进入该函数而n不会.因此, s就类似于VC等工具中的”step in”, n就类似于VC等工具中的”step over”.

查看变量值

键入p(print的缩写)+变量名即可查看该变量在此时的值

 Linux下C/C++语言的调试 gdb工具 笔记_第3张图片

4.从指定行开始运行

 r 行号

Linux下C/C++语言的调试 gdb工具 笔记_第4张图片

5.带有命令行参数的gdb调试


================================================================================================

详解coredump  摘录

地址来源:http://blog.csdn.net/tenfyguo/article/details/8159176/


1.设置corefile文件大小 格式和路径

(1)设置core文件大小

[root@localhost gdbtest]# ulimit -c unlimited

[root@localhost gdbtest]# ulimit -a
core file size          (blocks, -c) unlimited

但当前设置的ulimit只对当前会话有效,若想系统均有效,则需要进行如下设置:
在/etc/profile中加入以下一行,这将允许生成coredump文件
ulimit-c unlimited

 (2)设置core文件路径和格式

使程序崩溃时生成的coredump文件位于/data/coredump/目录下

使用下面的命令使kernel生成名字为core.filename.pid格式的core dump文件:
[root@localhost gdbtest]# echo "/home/shentan/backend_dev/gdbtest/core.%e.%p" >/proc/sys/kernel/core_pattern
[root@localhost gdbtest]# cat /proc/sys/kernel/core_pattern
/home/shentan/backend_dev/gdbtest/core.%e.%p

2.gdb定位coredump文件

(1)运行程序  生成core文件

[root@localhost gdbtest]# ./test 
0
Floating point exception (core dumped)
[root@localhost gdbtest]# ll
total 152
-rw-------. 1 root    root    290816 Mar 14 08:16 core.test.2635
-rwxrwxr-x. 1 shentan shentan  15592 Mar 14 07:32 test
-rw-rw-r--. 1 shentan shentan    213 Mar 14 07:32 test.cpp

(2)gdb定位core文件

[root@localhost gdbtest]# gdb ./test core.test.2635
GNU gdb (GDB) Fedora (7.3.50.20110722-9.fc16)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
...
Reading symbols from /home/shentan/backend_dev/gdbtest/test...done.
[New LWP 2635]
Missing separate debuginfo for 
Try: yum --disablerepo='*' --enablerepo='*-debuginfo' install /usr/lib/debug/.build-id/db/8dfab1cb00f1914573d3aa11e942b2d2a9b946
Core was generated by `./test'.
Program terminated with signal 8, Arithmetic exception.

#0  0x0804867f in main () at test.cpp:12
12 cout << 5/0 << endl;
Missing separate debuginfos, use: debuginfo-install glibc-2.14.90-14.i686 libgcc-4.6.2-1.fc16.i686 libstdc++-4.6.2-1.fc16.i686
(gdb) 

3.coredump产生的几种可能情况

造成程序coredump的原因有很多,这里总结一些比较常用的经验吧:

 1,内存访问越界

  a) 由于使用错误的下标,导致数组访问越界。

  b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符。

  c) 使用strcpy, strcat, sprintf, strcmp,strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。

 2,多线程程序使用了线程不安全的函数。

应该使用下面这些可重入的函数,它们很容易被用错:

_r函数

asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n)ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c)getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c)fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c)getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3)getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n)nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3)getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c)getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c)getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)

 3,多线程读写的数据未加锁保护。

对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成coredump

 4,非法指针

  a) 使用空指针

  b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump。

 5,堆栈溢出

不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。


4.gdb命令摘录

l 列出函数代码及其行数

b 16 在代码16行处设置断点

b func 在函数func处设置断点

r 运行程序  --到第一个断点处停止,使用n继续执行

n 单条执行语句

p i 打印i变量的值

bt  查看函数的堆栈

finish 退出函数

q 结束调试


5.可重入函数和线程安全函数

参考:http://blog.csdn.net/lmh12506/article/details/7169361

(1)可重入函数

可重入函数,描述的是函数被多次调用但是结果具有可再现性
如果fun(),中,使用了static变量、返回全局变量、调用非可重入函数等等,带有全局性的操作,
都将会导致2次以上调用fun()的结果的不可再现性(当然,有些时候使用了static、全局变量等等,
不一定导致调用结果不可再现性)。只要使调用结果具有可再现性,那么该函数就是可重入的。
为了保证函数是可重入的,需要做到一下几点:
1,不在函数内部使用静态或者全局数据
2,不返回静态或者全局数据,所有的数据都由函数调用者提供
3,使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
4,如果必须访问全局数据,使用互斥锁来保护 <---
5,不调用不可重入函数


可重入函数也可以这样理解,重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),

这样的函数就是purecode(纯代码)可重入,可以允许有多个该函数的副本在运行,由于它们使用的是分离的栈,所以不会互相干扰。

如果确实需要访问全局变量(包括static),一定要注意实施互斥手段。可重入函数在并行运行环境中非常重要,但是一般要为访问全局变量付出一些性能代价。

编写可重入函数时,若使用全局变量,则应通过关中断、信号量(即P、V操作)等手段对其加以保护。
若对所使用的全局变量不加以保护,则此函数就不具有可重入性,即当多个进程调用此函数时,很有可能使有关全局变量变为不可知状态。

(2)线程安全函数

如果一个函数能够安全的同时被多个线程调用而得到正确的结果,那么,我们说这个函数是线程安全的
所谓安全,一切可能导致结果不正确的因素都是不安全的调用。

线程安全,是针对多线程而言的。那么和可重入联系起来,我们可以断定,可重入函数必定是线程安全的,但是线程安全的,不一定是可重入的。
不可重入函数,函数调用结果不具有可再现性,可以通过互斥锁等机制,使之能安全的同时被多个线程调用,那么,这个不可重入函数就是转换成了线程安全。

(3)二者关系

可重入函数必定是线程安全的,但是线程安全的,不一定是可重入的。


你可能感兴趣的:(C++后台开发,linux)