Nginx代码调试——gdb工具

参考网上的资料,写了一个configprint模块,其功能为打印输出location配置内容,并计数访问次数。

代码链接如下:https://github.com/PaulWeiHan/nginx_module_development

程序的编写到运行总不是一帆风顺的,编译通过,运行不过的情况是最让我抓狂的。

这里记录一下gdb调试过程。供大家参考:

(这里没有gdb命令说明,请自行百度)

我使用的是nginx的默认模式即:

daemon on;
master_process on;
worker_processes  1;

我们知道。nginx默认执行的时候,是以daemon模式运行在后台,并且,由master进程fork出多个work子进程来监听端口的。切到nginx目录,执行下面命令:

1 gdb
2 shell sbin/nginx
3 shell pidof nginx

这样,我们就可以得到nginx的pid,一般会是两个,由于worker子进程是fork master得来的,所以自然worker进程的pid较大。利用gdb的attach和detach命令追踪worker子进程。

(gdb) shell pidof nginx
11903 11902
(gdb) attach 11903
Attaching to process 11903
Reading symbols from /home/renwh/nginx/sbin/nginx...done.
Reading symbols from /lib64/libpthread.so.0...(no debugging symbols found)...done.
[Thread debugging using libthread_db enabled]
Loaded symbols for /lib64/libpthread.so.0
Reading symbols from /lib64/libcrypt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libcrypt.so.1
Reading symbols from /lib64/libpcre.so.0...(no debugging symbols found)...done.
Loaded symbols for /lib64/libpcre.so.0
Reading symbols from /lib64/libz.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libz.so.1
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Reading symbols from /lib64/libfreebl3.so...(no debugging symbols found)...done.
Loaded symbols for /lib64/libfreebl3.so
Reading symbols from /lib64/libdl.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libdl.so.2
Reading symbols from /lib64/libnss_files.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_files.so.2
0x00000031684e8fb3 in __epoll_wait_nocancel () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.149.el6.x86_64 nss-softokn-freebl-3.14.3-17.el6.x86_64 pcre-7.8-6.el6.x86_64 zlib-1.2.3-29.el6.x86_64
(gdb) 

子进程即worker进程在运行后会停留在epoll_wait处等待相应的事件发生,而这个函数调用被封装在ngx_process_events_and_timers 中。于是我们在这个函数出设置一个断点:b ngx_process_events_and_timers.然后采用命令c,使得nginx一直运行,直到遇到第一个断点:

1 (gdb) b ngx_process_events_and_timers
2 Breakpoint 1 at 0x41b565: file src/event/ngx_event.c, line 195.
3 (gdb) c
4 Continuing.
5 
6 Breakpoint 1, ngx_process_events_and_timers (cycle=0x9be050) at src/event/ngx_event.c:195
7 195     {
8 (gdb) 

进去之后,一直用n向下运行,直到找到ngx_process_events,然后用s追踪进该函数,一直n,直到运行到epoll_wait,发现,停在了这里,我们知道work子进程在等待事件了。

Breakpoint 1, ngx_process_events_and_timers (cycle=0x9be050) at src/event/ngx_event.c:195
195     {
(gdb) n
199         if (ngx_timer_resolution) {
(gdb) n
204             timer = ngx_event_find_timer();
(gdb) n
218         if (ngx_use_accept_mutex) {
(gdb) n
240         delta = ngx_current_msec;
(gdb) n
242         (void) ngx_process_events(cycle, timer, flags);
(gdb) s
ngx_epoll_process_events (cycle=0x9be050, timer=18446744073709551615, flags=1)
    at src/event/modules/ngx_epoll_module.c:702
702     {
(gdb) n
717         events = epoll_wait(ep, event_list, (int) nevents, timer);
(gdb) n

这时候,你只需要打开一个浏览器,访问你配置好的nginx模块的url,然后你就会发现gdb可以向下运行了。这时候,你需要在你自己的handler函数处设置断点,然后c,你会发现,又一次调用了ngx_process_events_and_timers函数,s进入函数,继续c运行,然后gdb会停在你自己的handler函数入口,这里是:ngx_http_configprint_handler。s追踪进去,你就可以单步执行你自己写的handler函数,进行debug了。

 1 (gdb) b ngx_http_configprint_handler
 2 Breakpoint 2 at 0x4699a7: file /home/renwh/src/nginx-1.9.2/configprint//ngx_http_configprint_module.c, line 184.
 3 (gdb) c
 4 Continuing.
 5 
 6 Breakpoint 1, ngx_process_events_and_timers (cycle=0x9be050) at src/event/ngx_event.c:195
 7 195     {
 8 (gdb) s
 9 199         if (ngx_timer_resolution) {
10 (gdb) n
11 204             timer = ngx_event_find_timer();
12 (gdb) n
13 218         if (ngx_use_accept_mutex) {
14 (gdb) n
15 240         delta = ngx_current_msec;
16 (gdb) c
17 Continuing.
18 
19 Breakpoint 2, ngx_http_configprint_handler (r=0x9c8550)
20     at /home/renwh/src/nginx-1.9.2/configprint//ngx_http_configprint_module.c:184
21 184     {
22 (gdb) n
23 189             u_char ngx_my_string[1024] = {0};
24 (gdb) n
25 193             ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "ngx_http_configprint_handler is

gdb不光可以帮你debug,还可以帮你理解整个nginx的运行过程。你可以尝试在自己的handler挂载函数,loc_conf创建函数等地方添加断点,来查看整个模块的调用过程。

个人理解,欢迎讨论。联系方式:rwhsysu@163.com