Debugging nginx with DTrace pid provider
使用DTrace pid提供程序调试nginx
本文假定读者具有关于nginx内部和DTrace的一般知识。
尽管使用--with-debug选项构建的nginx已经提供了很多关于请求处理的信息,但有时候希望更全面地跟踪代码路径的特定部分,同时省略其余的调试输出。DTrace pid提供程序(可在Solaris,macOS上使用)是探索用户程序内部的有用工具,因为它不需要任何代码更改,并且可以帮助执行任务。跟踪和打印nginx函数调用的简单DTrace脚本可能如下所示:
#pragma D option flowindent
pid$target:nginx::entry {
}
pid$target:nginx::return {
}
但是,函数调用跟踪的DTrace功能仅提供有限的有用信息。实时检查函数参数通常更有趣,但也更复杂一些。下面的示例旨在帮助读者更熟悉DTrace以及使用DTrace分析nginx行为的过程。
与nginx一起使用DTrace的常见方案之一是:附加到nginx工作进程以记录请求行并请求开始时间。相应的附加函数是ngx_http_process_request()
,并且所讨论的参数是指向ngx_http_request_t
结构的指针。这种请求记录的DTrace脚本可以像下面这样简单:
pid$target::*ngx_http_process_request:entry
{
this->request = (ngx_http_request_t *)copyin(arg0, sizeof(ngx_http_request_t)
this->request_line = stringof(copyin((uintptr_t)this->request->request_line.data,
this->request->request_line.len)
printf("request line = %s\n", this->request_line
printf("request start sec = %d\n", this->request->start_sec
}
应该注意的是,在上面的例子中,DTrace需要一些关于ngx_http_process_request结构的知识。 不幸的是,虽然可以在DTrace脚本中使用特定的#include指令,然后将其传递给C预处理器(带有-C标志),但这并不起作用。 由于很多交叉依赖关系,几乎所有的nginx头文件都必须包含在内。 反过来,基于配置脚本设置,nginx头文件将包含PCRE,OpenSSL和各种系统头文件。 虽然理论上所有与特定nginx构建相关的头文件都可能包含在DTrace脚本预处理和编译中,但实际上,由于某些头文件中的未知语法,DTrace脚本很可能无法编译。
上面的问题可以通过在DTrace脚本中只包含相关的和必要的结构和类型定义来解决。DTrace必须知道结构,类型和字段偏移的大小。因此,通过手动优化用于DTrace的结构定义可以进一步降低依赖性。
让我们使用上面的DTrace脚本示例,看看它需要哪些结构定义才能正常工作。
首先应该包含由configure生成的objs / ngx_auto_config.h文件,因为它定义了一些影响各种#ifdef的常量。 之后,应该将一些基本类型和定义(如ngx_str_t,ngx_table_elt_t,ngx_uint_t等)放在DTrace脚本的开头。 这些定义是紧凑的,通常使用的,不太可能经常改变。
而且ngx_http_process_request_t
是包含很多指向其他结构的指针的结构。因为这些指针与这个脚本真的无关,并且因为它们具有相同的大小,所以可以用void指针替换它们。不过,不要改变定义,但最好添加适当的typedef:
typedef ngx_http_upstream_t void;
typedef ngx_http_request_body_t void;
最后但并非最不重要的是,需要添加两个成员结构(ngx_http_headers_in_t
,ngx_http_headers_out_t
)的定义,回调函数的声明和常量的定义。
最终的DTrace脚本可以从这里下载。
以下示例显示了运行此脚本的输出:
# dtrace -C -I ./objs -s trace_process_request.d -p 4848
dtrace: script 'trace_process_request.d' matched 1 probe
CPU ID FUNCTION:NAME
1 4 .XAbmO.ngx_http_process_request:entry request line = GET / HTTP/1.1
request start sec = 1349162898
0 4 .XAbmO.ngx_http_process_request:entry request line = GET /en/docs/nginx_dtrace_pid_provider.html HTTP/1.1
request start sec = 1349162899
使用类似的技术,读者应该能够追踪其他nginx函数调用。