在实际开发中,当遇见程序出现bug,无法一眼看出问题点时,需要使用调试的方法来找出原因。调试的方法有以下两种:
- 在程序中插入打印语句,优点是能够显示程序的动态过程,比较容易检查源程序的有关信息;缺点是效率低、可能输入大量无关的数据,发现问题具有偶然性。
- 借助调试工具。
下面我们主要来熟悉下各种调试工具的使用,方便更高效的进行调试
1. strace 工具
strace 就是一个通过跟踪系统调用来让开发者知道一个程序在后台所做事情的工具。
每一行都是一次系统调用,等号的左边是系统调用的函数名及其参数,右边是该调用的返回值。
- strace 不光能追踪系统调用,通过使用参数-c,它还能将进程所有的系统调用做一个统计分析并返回。
- 参数-o用在将strace的结果输出到文件中
- 参数-T将每个系统调用所花费的时间打印出来,每个调用的时间花销都体现在调用行最右边的尖括号里面
- 参数-t、-tt、-ttt则是记录每次系统调用发生的时间,分别精确到秒、微秒和UNIX时间戳的微秒
- strace不光能自己初始化一个进程进行strace,还能追踪现有进程,参数-p就是取这个作用,格式:strace -p pid (pid是指进程id)
2. gdb工具
- 启动gdb的方法
(1)gdb program
(2)gdb program core(用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件)
(3)gdb program 1234(如果程序是一个服务程序,那么可以指定这个服务程序运行时的进程ID,gdb会自动进行attach操作,并调试这个程序。并且program应该在PATH环境变量中搜索得到) - gdb的命令
(1)l:列出函数代码及其行数
(2)b 16:在代码16行处设置断点
(3)b func:在函数func处设置断点
(4)r:运行程序
(5)n:单条执行语句
(6)p i:打印i变量的值
(7)bt:查看函数的堆栈
(8)finish:退出函数
(9)q:结束调试 - coredump文件的存储路径
(1)在/proc/sys/kernel/core_pattern文件中可以看到core文件的存储位置,默认值是core,也就是当前目录,如果不是core,则是在指定的目录下
(2)通过修改kernel的参数,可以指定内核所生成的coredump文件的文件名。eg:使用echo “/data/coredump/core.%e.%p” > /proc/sys/kernel/core_pattern可以修改core文件名的格式为core.filename.pid。
(3)当/proc/sys/kernel/core_uses_pid文件中的内容被置为1是,core_pattern中没有设置%p,最后生成的core dump文件名仍会加上进程ID - 产生coredump文件的条件
(1)首先确认当前会话的能生成的coredump文件大小(ulimit -c:查看coredump文件大小的最大值,为0时不会产生对应的coredump文件;ulimit -c unlimited:设置coredump文件的大小为不受限制;ulimit -c [size]:设置对应的字符大小,size的单位是blocks,一般1 blocks = 512Bytes),当前设置的ulimit只对当前会话有效,若想系统均有效则在/etc/profile中加入对应的设置eg:ulimit -c unlimited
(2)当前用户具有写入core目录的写权限以及有足够的空间
(3)产生coredump文件的原因
–>1.内存访问越界(I.数组访问越界;II。搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符;III.使用strcpy,strcat,sprintf,strcasecmp等字符串操作函数时,容易出现将目标字符串读、写越界的情况,应改用strncpy,strlcpy,strncat,strlcat,snprintf,strncmp,strcasecmp等函数防止读写越界)
–>2.多线程使用线程不安全的函数
–>3.多线程读写的数据未加锁保护
–>4.非法指针,包括使用空指针或随意使用指针转换或野指针
–>5.堆栈溢出
3. top 工具
top命令是Linux系统下常用的性能分析工具,能够实时显示系统中各个进程的资源占用情况,类似于windows的任务管理器。
- 各列所代表的意思,请自行查阅相关资料
- buffers指的是块设备的读写缓冲区;cached指的是文件系统本身的页面缓存。
4. ps 工具
Linux中的ps(process status)命令列出的是当前在运行的进程的快照
- linx上进程的5中状态
(1)运行(正在运行或在运行队列中等待)
(2)中断(休眠中,受阻,在等待某个条件的形成或接受到信号)
(3)不可中断(收到信号不唤醒和不可运行,进程必须等待直到有中断发生)
(4)僵死(进程已终止,但进程描述符存在,直到父进程调用wait4()系统调用后释放)
(5)停止(进程收到SIGSTOP,SIGSTP,SIGTIN,SIGTOU信号后停止运行) - ps工具标识进程的5种状态码:
(1)D不可中断:uninterruptible sleep(usually IO)
(2)R 运行:runnable(on run queue)
(3)S 中断:sleeping
(4)T 停止:traced or stopped
(5)Z 僵死:a defunct(“zombie”)process - 显示指定用户信息 (eg:ps -u root)
- 显示所有进程信息 (eg:ps -ef)
- 查找特定进程 (eg:ps -ef | grep test)
- 将目前登录的PID与相关信息列出来 (ps -l)
- 列出目前所有的正在内存当中的程序(tty为 pts/0等,标识由网络连接进主机的程序)
5. 内存分析工具—Valgrind
-
Valgrind包括如下工具:
(1)Memcheck:内存检查器
(2)Callgrind:和gprof类似的分析工具,但它对程序的运行观察更是入微,能够提供更多的信息
(3)Cachegrind:检查程序中缓存使用出现的问题
(4)Helgrind:检查多线程程序中出现的竞争问题
(5)Massif:堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块、堆管理块和栈的大小
(6)Extension:可以利用core提供的功能,自己编写特定的内存调试工具 -
C程序内存空间的组成
(1)代码段(.text segment):存放程序执行代码
(2)初始化数据段(.data segment):存放程序中已初始化的全局变量
(3)未初始化数据段(.bss segment):存放程序中未初始化的全局变量(bss的全称:Block Started by Symbol)
(4)堆(heap):进程运行中被动态分配的内存段
(5)栈(stack):存放程序的局部变量,不包括static变量,static存放在数据段 -
Valgrind的官网地址:http://www.valgrind.org
-
Valgrind的使用
Valgrind默认的工具是Memcheck,也可以通过“–tool=tool name”之路指定其他的工具 -
常见的内存错误情况
(1)使用未初始化的内存
(2)内存读写越界
(3)内存覆盖
(4)动态内存管理错误(I.申请和释放不一致,使用函数不匹配;II.申请和释放不匹配,多释放或少释放;III.释放后仍然读写)
(5)内存泄露
memcheck将内存泄露分为两种,一种是可能的内存泄露(possibly lost),一种是确定的内存泄露(definitely lost)。可能的内存泄露是指仍然存在某个指针能够访问某块内存,但该指针指向的已经不是该内存首地址。确定的内存泄露是指已经不能访问这块内存。而确定的内存泄露又分为两种:直接的(direct)和间接地(indirect).直接和间接地区别就是,直接是没有任何指针指向该内存;间接是指指向该内存的指针都位于内存泄露处。