在学习优秀的源代码时是少不了源码的跟踪与调试,它不仅是我们解决程序bug的有效途径,也是我们理解、学习优秀源码的有效途径。

本文主要介绍一些源码调试的方法,并结合Nginx源码进行示例。

1,利用GDB调试

a,首先你应该熟悉GDB调试的一些基本命令(不熟悉的移步 至用GDB调试程序 ,熟悉step,run,break,list,info,continue等命令)。

b.下载nginx源码,这里使用nginx-1.0.14,解压文件。其中auto文件夹里包含了configure运行时的各种命令集合,src是源码。为了利用

GDB调试Nginx,需要在生成Nginx程序时把-g编译选项打开。我们需要修改 auto/cc/conf文件把ngx_compile_opt="-c" 加上 -g 选项

变为ngx_compile_opt="-c -g",下一步执行configure命令:sudo ./configure,然后运行命令:vim objs/Makefile确认一下-g参数是否加上了。如下图:

-g编译选项已打开,然后执行命令:sudo make .(如果之前已经执行过make,那么第二次make时需要确保能够重新编译,此时可以通过刷新所有源文件时间戳,间接达到重新编译出一个新的Nginx可执行程序,命令为:find . -name "*.c" | xargs touch),好了,Nginx编译成功。

c.启动nginx,在objs目录下执行命令:sudo ./nginx,成功运行nginx后执行命令:ps -ef | grep nginx,查看nginx的master及worker进程的PID,如果对nginx工作进程2177进行gdb调试,那么可以利用gdb的-p选项。命令:gdb -p 2177.

当有多个工作进程时 调试起来比较麻烦。我们可以修改配置项,修改文件nginx.conf。加入master_process off;单进程模式 将监控进程和工作进程逻辑全部合在一个进程里。此时只有一个进程,可以方便的利用gdb进行调试。

我们知道工作进程会停留在epoll_wait处等待相应的事件发生,而这个函数调用被封装在ngx_process_events_and_times中,于是我们在这个函数设置一个断点: b ngx_event.c:ngx_process_events_and_times,结合gdb命令c,s,n,如图所示:

采用命令c,使得nginx一直运行,直到遇到第一个断点,处理事件的方法是ngx_process_events,于是我们用命令s跟踪进去这个函数。当执行到epoll_wait函数的时候,发现进程停留在这里,不能在向下执行。这就证明了worker子进程阻塞在epoll_wait函数调用处。此时我们在另一个终端执行下列命令,以向nginx发送消息: curl -I localhost,可以看到请求已经发送,正在等待回应。

此时继续执行命令 c即可在另一终端得到回应。此时可以通过bt命令查看单进程模式下函数调用的过程。如图。

利用curl命令。

gdb bt

2,利用strace、pstack调试nginx

a.strace常用来跟踪进程执行时的系统调用和所接收的信号。在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘问及那,接收网络数据等等)时,必须由用户态模块切换至内核态模式,通过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。此外命令ltrace用来查看 动态库函数调用。

b.那具体是哪个函数调用呢?在strace输出结果中并不能找到答案,因其输出显示都是系统调用,要显示程序中函数调用栈信息,就轮到pstack上场了。pstack是一个脚本工具,其核心实现就是使用了gdb以及thread apply all bt命令。

我们在此修改nginx的配置文件nginx.conf使其为仅具有一个master进程和一个worker进程。

把1中修改的master_process off;注解掉加上#

并在配置文件上加上worker_processes 1;

关闭之前的nginx重新启动nginx。利用命令:ps -ef | grep nginx查看当前存在的nginx进程,然后用strace命令的参数-p 选项跟踪Nginx工作进程,如图。

可以看到工作进程阻塞在epoll_wait系统调用上,因为此时没有客户端请求,nginx就阻塞于此,在另一个终端执行curl命令:curl -I localhost,再来看strace输出结果如图。

strace输出的每一行记录一次系统调用,等号左边是系统调用名以及调用参数,等号右边是该系统调用的返回值。

通过上面的系统调用我们可以做出如下分析:

⑴.   epoll_wait返回值为1,表示有1个描述符存在可读/写事件,这里当然是可读事件。
 ⑵.   accept4接受该请求,返回的数字3表示socket的文件描述符。
 ⑶.   epoll_ctl把accept4建立的socket套接字(注意参数3)加入到事件监听机制里。
 ⑷.   recv从发生可读事件的socket文件描述符内读取数据,读取的数据存在第二个参数内,读取了79个字节。
 ⑸.   stat64判断客户端请求的html文件是否存在,返回值为0表示存在。
 ⑹.   open/fstat64打开并获取文件状态信息。open文件返回的文件描述符为9,后面几个系统调用都用到这个值。
 ⑺.   writev把响应头通过文件描述符3代表的socket套接字发给客户端。
 ⑻.   sendfile64把文件描述符9代表的响应体通过文件描述符3代表的socket套接字发给客户端。
 ⑼.   再往文件描述符4代表的日志文件内write一条日志信息。
 ⑽.   recv看客户端是否还发了其它待处理的请求/信息。
 ⑾.   最后关闭文件描述符3代表的socket套接字。
  由于strace能够提供nginx执行过程中的这些内部信息,所以在出现一些奇怪现象,比如nginx启动失败、响应的文件数据和预期不一致、莫名其妙

的Segment Fault段错误、存在性能瓶颈(利用-T选项跟踪各个函数的消耗时间),利用strace也许能提供一些相关帮助。最后,要退出strace跟踪,按ctrl+c即可。

详细介绍strace 参数:

-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出,以秒了表示时间.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column
设置返回值的输出位置.默认 为40.
-e expr
指定一个表达式,用来控制如何跟踪.格式如下:
[qualifier=][!]value1[,value2]...
qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如:
-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none.
注意有些shell使用!来执行历史记录里的命令,所以要使用\\.
-e trace=set
只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.
-e trace=file
只跟踪有关文件操作的系统调用.
-e trace=process
只跟踪有关进程控制的系统调用.
-e trace=network
跟踪与网络有关的所有系统调用.
-e strace=signal
跟踪所有与系统信号有关的 系统调用
-e trace=ipc
跟踪所有与进程通讯有关的系统调用
-e abbrev=set
设定 strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
-e raw=set
将指 定的系统调用的参数以十六进制显示.
-e signal=set
指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
-e read=set
输出从指定文件中读出 的数据.例如:
-e read=3,5
-e write=set
输出写入到指定文件中的数据.
-o filename
将strace的输出写入文件filename
-p pid
跟踪指定的进程pid.
-s strsize
指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
-u username
以username 的UID和GID执行被跟踪的命令

strace 通用的完整用法:

strace -o output.txt -T -tt -e trace=all -p 10423
上面的含义是 跟踪28979进程的所有系统调用(-e trace=all),并统计系统调用的花费时间,以及开始时间(并以可视化的时分秒格式显示),最后将记录结果存在
output.txt文件里面。

限制strace只跟踪特定的系统调用:

如果你已经知道你要找什么,你可以让strace只跟踪一些类型的系统调用。例如,在nginx执行程序时,你需要监视的系统调用epoll_wait。

让strace只记录epoll_wait的调用用这个命令:

strace -f -o epoll-strace.txt -e epoll_wait -p 10423

命令strace跟踪的是系统调用,对于nginx本身的函数调用关系无法给出更为明朗的信息,如果我们发现nginx当前运行不正常,想知道nginx当前内部到底在执行什么函数,

那么命令pstack就是一个非常方便实用的工具。pstack的使用也非常简单,后面跟进程id即可,比如在无客户端请求的情况下,nginx阻塞在epoll_wait系统调用处,此时

利用pstack查看到的nginx函数调用堆栈关系如下:

从main()函数到epoll_wait()函数的调用关系一目了然,和在gdb内看到的堆栈信息一样。我们可以利用此进行分析优化等。

除了 1,2 还有方法 加桩调试等方法在此不再叙述。以后有机会可以介绍下特殊应用逻辑的调试。

nginx源码分析--使用GDB调试相关推荐

  1. nginx源码分析—内存池结构ngx_pool_t及内存管理

    本博客( http://blog.csdn.net/livelylittlefish)贴出作者(阿波)相关研究.学习内容所做的笔记,欢迎广大朋友指正! Content 0.序 1.内存池结构 1.1 ...

  2. Nginx源码分析链接

    nginx-0.8.38源码探秘:http://blog.csdn.net/ccdd14/article/details/5872312 nginx源码分析: http://blog.sina.com ...

  3. Nginx源码分析:epoll事件处理模块概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 事件处理模块概述 Nginx的高效请求的处理依赖于事件管理机制,本次默认的场景是Linux操 ...

  4. Nginx源码分析:惊群处理与负载均衡

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> Nginx的惊群处理与负载均衡概述 当Nginx工作在master/worker模式下时,就 ...

  5. Nginx源码分析:核心数据结构ngx_cycle_t与内存池概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 核心数据结构与内存池概述 在Nginx中的核心数据结构就是ngx_cycle_t结构,在初始 ...

  6. Nginx源码分析:master/worker工作流程概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> Nginx的master与worker工作模式 在生成环境中的Nginx启动模式基本都是以m ...

  7. Nginx源码分析:启动流程

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> nginx简介 Nginx的作为服务端软件,表现的主要特点是更快.高扩展.高可靠性.低内存消 ...

  8. Nginx 源码分析-- 模块module 解析执行 nginx.conf 配置文件流程分析 一

    搭建nginx服务器时,主要的配置文件 nginx.conf 是部署和维护服务器人员经常要使用到的文件, 里面进行了许多服务器参数的设置.那么nginx 以模块 module为骨架的设计下是如何运用模 ...

  9. Nginx源码分析-内存池

    本文转自淘宝平台http://www.tbdata.org/archives/1390,不是为了夺他人之美,只是觉得写得很好,怕淘宝万一删掉就找不到了,放在这里保存一下.大家可以直接链接过去,他们那个 ...

最新文章

  1. 第五期直播《聊聊目标检测和秋招那些事》精彩回顾
  2. 高频焊台源码,改进版V2
  3. python 笔记:爱因斯坦求和 einsum
  4. QT快速入门、三点求圆心实现详解
  5. Hystrix Health Indicator及Metrics Stream支持
  6. why there is always popup window for HANA
  7. BFS,优先队列优化
  8. java中boolean类型占几个字节
  9. springwebflux 页面_【SpringBoot WEB系列】WebFlux静态资源配置与访问
  10. js判断url链接是否可访问(服务可连接,可用)
  11. vue 判断移动端、pc端
  12. Android 获取唯一机器码的代码
  13. express 模板 及 文件上传
  14. 谭浩强c语言程序60题,谭浩强版C语言课后经典习题解答100例
  15. android swf 文件播放器,swf播放器怎么用?swf播放器安卓版使用教程
  16. 超详细讲解ArcGIS拓扑分析(附路网练习数据下载)
  17. java中怎么自己画地图_用 4 行代码画一幅中国地图
  18. 高中计算机考试实施方案,山东省高中信息技术学业水平考试实施方案及思考
  19. Linux之pure-ftpd安装和使用
  20. EMC 设计经验总结

热门文章

  1. mysql 查看端口_新手连接MySQL数据库,再也不怕连不上了
  2. Effective Java之多个构造参数考虑用构建器(二)
  3. 洛谷——P1071 潜伏者
  4. 字符串关键字的散列映射 (25 分)【详细解析】
  5. DbUtils使用原理详解【不懂的来】
  6. L1-005 考试座位号 (15分) C语言实现(18行代码AC!)
  7. 正则表达式(grep命令,egrep命令,sed命令,awk命令,sort工具,uniq工具)
  8. Shell脚本函数(函数传参、递归、创建库)
  9. c语言源程序要求每行只能书写一条语句,C语言章节习题集(全)
  10. STM32的时钟配置——时钟树解析