使用 DTrace pid provider 除錯 nginx
本文假設讀者對 nginx 內部機制和 DTrace 有一般了解。
儘管使用 --with-debug 選項構建的 nginx 已經提供了關於請求處理的大量資訊,但有時我們希望更徹底地追蹤特定程式碼路徑的部分,同時省略其餘的除錯輸出。DTrace pid provider(在 Solaris、macOS 上可用)是一個有用的工具,可以探索使用者空間程式的內部機制,因為它不需要任何程式碼變更,並且可以幫助完成這項任務。一個簡單的 DTrace 腳本來追蹤和列印 nginx 函式呼叫可能如下所示
#pragma D option flowindent pid$target:nginx::entry { } pid$target:nginx::return { }
然而,DTrace 追蹤函式呼叫的功能僅提供有限的有用資訊。對函式引數進行即時檢查通常更有趣,但也稍微複雜一些。以下範例旨在幫助讀者更熟悉 DTrace 以及使用 DTrace 分析 nginx 行為的過程。
將 DTrace 與 nginx 結合使用的常見情境之一如下:附加到 nginx worker 程序以記錄請求行和請求開始時間。要附加的對應函式是 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
結構的知識。不幸的是,雖然可以在 DTrace 腳本中使用特定的 #include
指令,然後將其傳遞給 C 前處理器(使用 -C
標誌),但這實際上並不可行。由於存在許多交叉依賴關係,幾乎必須包含所有 nginx 標頭檔。反過來,根據 configure
腳本設定,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_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 函式呼叫。
另請參閱