《深入剖析ngx》——调试

1. gdb调试

  1. 编译带调试信息的ngx
./configure --with-cc-opt='-g -O0'
CFLAGS='-g -O0' ./configure
make CFLAGS='-g -O0'
gdb -p 3333
(gdb)attach 3333

若要gdb支持查看宏定义,需要 将 -g 改为 -ggdb3

配置ngx以方便gdb

worker_processes 1;
set follow-fork-mode child;
daemon off;
master_process off;

gdb带参启动ngx

gdb --args ./nginx -c /usr/local/nginx/conf/nginx.conf

gdb ./nginx
set args -c /usr/local/nginx/conf/nginx.conf

2. 日志调试

  1. 编译时开启debug
    ./configure --with-debug 会生成 NGX_DEBUG 宏,以开启很多调试功能。

  2. 配置文件设置日志级别
    error_log logs/error.log error;
    使用debug级别会导致输出信息过多,可以设置粒度:debug_core, debug_http...

error_log 配置指令可以放在多个上下文中,如location, server, http, main

debug_connection 指令,设置针对特定连接记录日志
debug_connection 192.168.1.1;

3. strace 和 pstack

strace 查看系统调用
有用参数
-p pid 指定追踪进程
-o filename 输出文件
-f 追踪fork的子进程
-t 输出每个系统调用发起时间
-T 输出每个系统调用消耗时间

pstack 查看函数调用

4. 全局跟踪

添加 my_debug

#ifndef MY_DEBUG_LENKY_H
#define MY_DEBUG_LENKY_H
#include <stdio.h>
void enable_my_debug(void) __attribute__((no_instrument_function));
void disable_my_debug(void) __attribute__((no_instrument_function));
int get_my_debug_flag(void) __attribute__((no_instrument_function));
void set_my_debug_flag(int) __attribute__((no_instrument_function));
void main_constructor(void) __attribute__((no_instrument_function, constructor));
void main_destructor(void) __attribute__((no_instrument_function, constructor));
void __cyg_profile_func_enter(void *, void *) __attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *, void *) __attribute__((no_instrument_function));
#endif
#include "my_debug.h"
#include <stdio.h>
#define MY_DEBUG_FILE_PATH "/usr/local/ngx/mydebug.log"
int _flag = 0;
FILE *my_debug_fd;
#define open_my_debug_file() \
    (my_debug_fd = fopen(MY_DEBUG_FILE_PATH, "a"))

#define close_my_debug_file() \
    do { \
        if (NULL != my_debug_fd) { \
            fclose(my_debug_fd); \
        } \
    } while (0)

#define my_debug_print(fmt, ...) \
    do {\
        if (0 == _flag) {\
            break; \
        } \
        if (NULL == my_debug_fd && NULL == open_my_debug_file()) {\
            printf("Err: Can not open output file.\n"); \
            break; \
        } \
        fprintf(my_debug_fd, fmt, ##__VA_ARGS__); \
        fflush(my_debug_fd);\
    } while (0)

void enable_my_debug(void)
{
    _flag = 1;
}
void disable_my_debug(void)
{
    _flag = 0;
}
int get_my_debug_flag(void)
{
    return _flag;
}
void set_my_debug(int flag)
{
    _flag = flag;
}
void main_constructor(void)
{
    // Do Nothing
}
void main_destructor(void)
{
    close_my_debug_file();
}
void __cyg_profile_func_enter(void *this, void *call)
{
    my_debug_print("Enter\n%p\n%p\n", call, this);
}
void __cyg_profile_func_exit(void *this, void *call)
{
    my_debug_print("Exit\n%p\n%p\n", call, this);
}

gcc 增加 CFLAGS -finstrument-functions

这时 gcc 会在每个函数调用前,先调用 __cyg_profile_func_enter,
函数退出前先调用 __cyg_profile_func_exit。

在用 addr2line 转换地址为 文本

#!/bin/sh
if [ $# != 3 ]; then
    echo 'Usage: addr2line.sh executefile addressfile functionfile'
    exit
fi;
cat $2 | while read line
do
    if [ "$line" = 'Enter' ]; then
        read line1
        read line2
#       echo $line >> $3
        addr2line -e $1 -f $line1 -s >> $3
        echo "--->" >> $3
        addr2line -e $1 -f $line2 -s | sed 's/^/  /' >> $3
        echo >> $3
    elif [ "$line" = 'Exit' ]; then
        read line1
        read line2
        addr2line -e $1 -f $line2 -s | sed 's/^/  /' >> $3
        echo "<---" >> $3
        addr2line -e $1 -f $line1 -s >> $3
#       echo $line >> $3
        echo >> $3
    fi;
done

4. 总结

gdb特点是 单步调试
日志特点是 整体监控
pstack 和 strace特点是 工作流程分析

posted on 2022-03-10 15:20  开心种树  阅读(111)  评论(0编辑  收藏  举报