用gdb调试apache2.2和nginx

调试Apache2.2:
  1. 下载apache2.2源码:httpd-2.2.22.tar.gz
    http://projects.apache.org/projects/http_server.html
    wget http://mirror.bjtu.edu.cn/apache//httpd/httpd-2.2.22.tar.gz
  2. 解压:tar xf httpd-2.2.22.tar.gz
  3. 进入目录:cd httpd-2.2.22
  4. 生成Makefile:./configure
  5. 将优化选项关闭,保证调试时和源码对应:
    find . -name "*.mk"|xargs grep --color "\-O2"
    find . -name "*.mk"|xargs sed -i "s/-O2/-O0/g"

  6. 编译和安装:
    make
    sudo make install
  7. 以root启动调试,需要绑定端口权限(参数指定-X,为调试模式,只启动一个进程一个并发):
    sudo
    cd /usr/local/apache2/bin
    gdbtui httpd
    (gdb) b main
    (gdb) r -X

调试nginx1.0.15:

  1. 下载源码:
    http://nginx.org/en/download.html
    wget http://nginx.org/download/nginx-1.0.15.tar.gz
  2. 安装依赖库:
    sudo yum install -y pcre-devel.x86_64
  3. 解压和配置:
    tar xf nginx-1.0.15.tar.gz
    cd nginx-1.0.15
    ./configure
    find . -name "Makefile"|xargs grep --color "\-O\ "
    find . -name "Makefile"|xargs sed -i "s/-O /-O0 /g"
    find . -name "Makefile"|xargs grep --color "\-O"
    make
    sudo make install
  4. 启动调试:
    sudo
    cd /usr/local/nginx/sbin
    gdbtui nginx
    (gdb) b main
    (gdb) r

举个实例:

如何让nignx以单进程运行(不以master和worker,不进入后台)方便调试:

    main():408以单进程启动的代码:
        if (ngx_process == NGX_PROCESS_SINGLE) {ngx_single_process_cycle(cycle);}
    全局变量ngx_process赋值的地方只有一个 main():363
        if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) { ngx_process = NGX_PROCESS_MASTER;}
    所以,ccf->master为0即可单进程启动。这个是配置项,master在nginx.conf没有,所以看worker_processes是如何设置的。
    修改配置文件,将worker_processes改为3。启动调试,在变量cycle初始化之后添加观察点:
        gdb nginx
        b main
        r
        # line 333: cycle = ngx_init_cycle(&init_cycle);
        #进入main断点,运行到cycle初始化之后即333行
        u 334
        # 设置观察点
        watch (*(ngx_core_conf_t*)cycle->conf_ctx[0]).worker_processes
        u 362
        # 发现没有修改这个值。查看一下这个值:
        p (*(ngx_core_conf_t*)cycle->conf_ctx[0]).worker_processes
        # 已经变为了3,所以就是333行初始化的,所以进入ngx_init_cycle看看。
        # 删除所有的断点,重启配置
        delete
        b ngx_init_cycle
        r
        # 观察这个函数,找到cycle和ccf初始化的地方(notepad++选中cycle即可看出它在什么地方赋值),先执行到cycle初始化的地方:
        # line 79: cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));
        u 80
        p cycle->conf_ctx
        # 这个时候还没有初始化ccf,观察一下cycle->conf_ctx初始化的地方,执行到这里:
        # line 188: cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
        l 188
        u 189
        p (*(ngx_core_conf_t*)cycle->conf_ctx[0]).worker_processes
        # 发现这个时候conf_ctx还没有初始化为数组,所以再观察:
        # line 228: cycle->conf_ctx[ngx_modules[i]->index] = rv;
        u 228
        n
        p (*(ngx_core_conf_t*)cycle->conf_ctx[0]).worker_processes
        # 发现这个时候值为-1(还没有加载配置),所以可以设置观察点:
        watch (*(ngx_core_conf_t*)cycle->conf_ctx[0]).worker_processes
        # 观察可能改变这个值的代码,应该在line 268: ngx_conf_parse这个地方最有可能会改变,执行到这里:
        u 269
        # 这个时候发现改变了,是函数ngx_conf_set_num_slot设置了这个值,所以设置断点,重启程序:
        # Hardware watchpoint 30: (*(ngx_core_conf_t*)cycle->conf_ctx[0]).worker_processes
        # Old value = -1
        # New value = 3
        # ngx_conf_set_num_slot (cf=0x7fffffffe610, cmd=0x6bbaf8, conf=0x6dca70) at src/core/ngx_conf_file.c:1201
        delete
        b ngx_conf_set_num_slot
        r
        # 进入断点ngx_conf_set_num_slot,查看调用堆栈:
        bt
        #0  ngx_conf_set_num_slot (cf=0x7fffffffe610, cmd=0x6bbaf8, conf=0x6dca70) at src/core/ngx_conf_file.c:1186
        #1  0x000000000041c0e1 in ngx_conf_handler (cf=0x7fffffffe610, last=0) at src/core/ngx_conf_file.c:394
        #2  0x000000000041bc7f in ngx_conf_parse (cf=0x7fffffffe610, filename=0x6dbce0) at src/core/ngx_conf_file.c:244
        #3  0x00000000004189b0 in ngx_init_cycle (old_cycle=0x7fffffffe760) at src/core/ngx_cycle.c:268
        #4  0x00000000004038d0 in main (argc=1, argv=0x7fffffffea28) at src/core/nginx.c:333
        # 可以完全的发现如何调用的配置了。
        # ngx_conf_parse:173 rc = ngx_conf_read_token(cf); 读取一个配置项,可以设断点看看。
        # ngx_conf_handler:289 name = cf->args->elts; 取出配置名称:
        #                                           (gdb) p *name 
        #                                            $48 = {len = 16, data = 0x6dcc20 "worker_processes"}
        # ngx_conf_handler:303 cmd = ngx_modules[i]->commands; 循环处理所有的命令,查看这个commands,实际上就是全局变量 ngx_core_commands
        #                   在 ngx_core_commands 中可以发现,可以设置master_process来设置master,关于offsetof就是计算偏移量。
        # ngx_conf_handler:394 rv = cmd->set(cf, cmd, conf); 就是调用这个回调函数(ngx_core_commands中定义),进入 ngx_conf_set_num_slot
    修改配置文件,增加配置项 master_process off; 启动调试:
        delete
        b ngx_conf_set_flag_slot
        #进入断点,可以看到cmd为设置master:
        #                   (gdb) p *cmd
        #                   $50 = {name = {len = 14, data = 0x49d702 "master_process"}, type = 16843264, set = 0x41d58e <ngx_conf_set_flag_slot>, conf = 0, offset = 8, post = 0x0}
    直接设置main的断点,看作用如何:
        delete
        r
        u 362
        # 还需要设置daemon为off
        u 372
        n
        # ccf->master和ccf->daemon这时候为0,会以单进程启动:
        u 408
        # 发现调用了ngx_single_process_cycle(cycle);可以了。
        # origin:15132 master:0 worker:0 listen-80:(15132/nginx)


你可能感兴趣的:(apache,优化,makefile)