《深入剖析ngx》——调试
1. gdb调试
- 编译带调试信息的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. 日志调试
-
编译时开启debug
./configure --with-debug 会生成 NGX_DEBUG 宏,以开启很多调试功能。 -
配置文件设置日志级别
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特点是 工作流程分析
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?