Отладка nginx с помощью поставщика pid DTrace
Эта статья предполагает, что читатель обладает общими знаниями о внутреннем устройстве nginx и DTrace.
Хотя nginx, построенный с опцией --with-debug, уже предоставляет много информации о обработке запросов, иногда желательно более подробно проследить определенные части пути кода, одновременно исключив остальной вывод отладки. Поставщик pid DTrace (доступный в Solaris и macOS) — полезный инструмент для изучения внутреннего устройства программ пользовательского уровня, поскольку он не требует внесения каких-либо изменений в код и может помочь в этой задаче. Простой скрипт DTrace для отслеживания и печати вызовов функций nginx может выглядеть так:
#pragma D option flowindent
pid$target:nginx::entry {
}
pid$target:nginx::return {
}
Однако возможности DTrace для отслеживания вызовов функций предоставляют ограниченный объем полезной информации. Обычно более интересным, но и немного более сложным, является инспекция аргументов функций в реальном времени. Приведенные ниже примеры предназначены для того, чтобы помочь читателю лучше ознакомиться с DTrace и процессом анализа поведения nginx с помощью DTrace.
Один из распространенных сценариев использования DTrace с nginx заключается в следующем: подключение к процессу-рабочему 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_request_t. К сожалению, хотя можно использовать специфическую директиву #include в скрипте DTrace, а затем передать его препроцессору C (с флагом -C), это не работает должным образом. Из-за множества взаимных зависимостей почти все заголовочные файлы nginx необходимо включать. В свою очередь, в зависимости от настроек скрипта configure nginx-заголовки будут включать PCRE, OpenSSL и различные системные заголовочные файлы. Хотя теоретически все эти заголовочные файлы, относящиеся к конкретной сборке nginx, могут быть включены в предварительную обработку и компиляцию скрипта DTrace, на практике скрипт DTrace, скорее всего, не будет компилироваться из-за неизвестного синтаксиса в некоторых заголовочных файлах.
Вышеуказанную проблему можно решить, включив в скрипт DTrace только необходимые определения структур и типов. DTrace должен знать размеры структур, типы и смещения полей. Таким образом, зависимости можно далее уменьшить, вручную оптимизируя определения структур для использования с DTrace.
Давайте воспользуемся приведенным выше примером скрипта DTrace и посмотрим, какие определения структур ему нужны для корректной работы.
В первую очередь необходимо включить файл objs/ngx_auto_config.h, сгенерированный утилитой configure, поскольку он определяет ряд констант, влияющих на различные #ifdef. После этого в начале скрипта DTrace должны быть размещены некоторые базовые типы и определения, такие как ngx_str_t, ngx_table_elt_t, ngx_uint_t и т. д. Эти определения компактны, часто используются и вряд ли часто изменяются.
Затем идёт структура ngx_http_request_t, которая содержит множество указателей на другие структуры. Поскольку эти указатели действительно не относятся к данному скрипту и имеют одинаковый размер, их можно заменить указателями на void. Однако лучше добавить соответствующие типы вместо изменения определений:
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.
См. также
© 2002-2021 Igor Sysoev
© 2011-2024 Nginx, Inc.
Licensed under the BSD License.
https://nginx.org/en/docs/nginx_dtrace_pid_provider.html