檢視原始碼 追蹤工具建構器

簡介

追蹤工具建構器是為單節點或分散式 Erlang 系統建立追蹤工具的基礎。它需要被追蹤節點上可用的 Runtime_Tools 應用程式。

以下是追蹤工具建構器的主要功能:

  • 透過一個函式呼叫,在多個節點上開始追蹤到檔案埠。
  • 將更多資訊寫入追蹤資訊檔案,該檔案在格式化期間會被讀取。
  • 透過維護歷史緩衝區和處理組態檔案,來還原先前的組態。
  • 為循序追蹤提供一些簡單的支援。
  • 格式化二進位追蹤日誌,並合併來自多個節點的日誌。

追蹤工具建構器的目的是作為客製化追蹤工具的基礎,但它也可以直接從 Erlang shell 使用(它可以模擬 dbg 的行為,同時仍然提供有用的附加功能,例如比對規格捷徑)。追蹤工具建構器僅允許使用檔案埠追蹤器,因此若要使用其他類型的追蹤用戶端,最好直接使用 dbg

開始使用

模組 ttb 是追蹤工具建構器中所有函式的介面。

若要開始使用,您至少需要做的就是使用 ttb:tracer/0,1,2 啟動追蹤器,並使用 ttb:p/2 在您要追蹤的處理序上設定所需的追蹤旗標。

追蹤完成後,請使用 ttb:stop/0,1 停止追蹤器,並使用 ttb:format/1,2 格式化追蹤日誌(如果有的話)。

有用的函式

  • ttb:tracer/0,1,2 - 在每個要追蹤的節點上開啟追蹤埠。預設情況下,追蹤訊息會寫入遠端節點上的二進位檔案(二進位追蹤日誌)。

  • ttb:p/2 - 指定要追蹤的處理序。在此呼叫中指定的追蹤旗標會指定要在每個處理序上追蹤的內容。如果您希望在不同的處理序上設定不同的追蹤旗標,可以多次呼叫此函式。

  • ttb:tp/2,3,4ttb:tpl/2,3,4 - 如果您想要追蹤函式呼叫(也就是說,如果您在任何處理序上設定了追蹤旗標 call),您還必須使用 ttb:tp/2,3,4ttb:tpl/2,3,4 在所需的函式上設定追蹤模式。只有當函式具有追蹤模式時才會被追蹤。追蹤模式會指定如何使用比對規格追蹤函式。比對規格在 ERTS 使用者指南 中描述。

  • ttb:stop/0,1 - 停止所有節點上的追蹤,刪除所有追蹤模式,並刷新追蹤埠緩衝區。

  • ttb:format/1/2 - 將二進位追蹤日誌轉換為可讀取的內容。預設情況下,ttb 會將每個追蹤訊息呈現為一行文字,但您也可以編寫自己的處理常式來對追蹤資訊進行更複雜的解釋。追蹤日誌也可以使用應用程式事件追蹤器 (ET) 以圖形方式呈現。

    如果選項 format 指定給 ttb:stop/1,則在停止 ttb 時會自動完成格式化。

從 Erlang Shell 追蹤本機節點

以下的小模組用於後續範例

-module(m).
-export([f/0]).
f() ->
   receive
      From when is_pid(From) ->
         Now = erlang:now(),
         From ! {self(),Now}
   end.

以下範例顯示了從 Erlang shell 使用 ttb 的基本用法。追蹤器的啟動和格式化都使用預設選項(但是提供了自訂提取目錄)。這會在新建立的目錄中產生一個名為 Node-ttb 的追蹤日誌,其中 Node 是節點名稱。預設處理常式會在 shell 中列印格式化的追蹤訊息

(tiger@durin)47> %% First I spawn a process running my test function
(tiger@durin)47> Pid = spawn(m,f,[]).
<0.125.0>
(tiger@durin)48>
(tiger@durin)48> %% Then I start a tracer...
(tiger@durin)48> ttb:tracer().
{ok,[tiger@durin]}
(tiger@durin)49>
(tiger@durin)49> %% and activate the new process for tracing
(tiger@durin)49> %% function calls and sent messages.
(tiger@durin)49> ttb:p(Pid,[call,send]).
{ok,[{<0.125.0>,[{matched,tiger@durin,1}]}]}
(tiger@durin)50>
(tiger@durin)50> %% Here I set a trace pattern on erlang:now/0
(tiger@durin)50> %% The trace pattern is a simple match spec
(tiger@durin)50> %% indicating that the return value should be
(tiger@durin)50> %% traced. Refer to the reference_manual for
(tiger@durin)50> %% the full list of match spec shortcuts
(tiger@durin)50> %% available.
(tiger@durin)51> ttb:tp(erlang,now,return).
{ok,[{matched,tiger@durin,1},{saved,1}]}
(tiger@durin)52>
(tiger@durin)52> %% I run my test (i.e. send a message to
(tiger@durin)52> %% my new process)
(tiger@durin)52> Pid ! self().
<0.72.0>
(tiger@durin)53>
(tiger@durin)53> %% And then I have to stop ttb in order to flush
(tiger@durin)53> %% the trace port buffer
(tiger@durin)53> ttb:stop([return, {fetch_dir, "fetch"}]).
{stopped, "fetch"}
(tiger@durin)54>
(tiger@durin)54> %% Finally I format my trace log
(tiger@durin)54> ttb:format("fetch").
({<0.125.0>,{m,f,0},tiger@durin}) call erlang:now()
({<0.125.0>,{m,f,0},tiger@durin}) returned from erlang:now/0 ->
{1031,133451,667611}
({<0.125.0>,{m,f,0},tiger@durin}) <0.72.0> !
{<0.125.0>,{1031,133451,667611}}
ok

建立您自己的工具

以下範例顯示了一個用於「偵錯追蹤」的簡單工具,也就是追蹤具有傳回值的函式呼叫

-module(mydebug).
-export([start/0,trc/1,stop/0,format/1]).
-export([print/4]).
%% Include ms_transform.hrl so that I can use dbg:fun2ms/2 to
%% generate match specifications.
-include_lib("stdlib/include/ms_transform.hrl").
%%% -------------Tool API-------------
%%% ----------------------------------
%%% Star the "mydebug" tool
start() ->
    %% The options specify that the binary log shall be named
    %% <Node>-debug_log and that the print/4 function in this
    %% module shall be used as format handler
    ttb:tracer(all,[{file,"debug_log"},{handler,{{?MODULE,print},0}}]),
    %% All processes (existing and new) shall trace function calls
    %% We want trace messages to be sorted upon format, which requires
    %% timestamp flag. The flag is however enabled by default in ttb.
    ttb:p(all,call).

%%% Set trace pattern on function(s)
trc(M) when is_atom(M) ->
    trc({M,'_','_'});
trc({M,F}) when is_atom(M), is_atom(F) ->
    trc({M,F,'_'});
trc({M,F,_A}=MFA) when is_atom(M), is_atom(F) ->
    %% This match spec shortcut specifies that return values shall
    %% be traced.
    MatchSpec = dbg:fun2ms(fun(_) -> return_trace() end),
    ttb:tpl(MFA,MatchSpec).

%%% Format a binary trace log
format(Dir) ->
    ttb:format(Dir).

%%% Stop the "mydebug" tool
stop() ->
    ttb:stop(return).

%%% --------Internal functions--------
%%% ----------------------------------
%%% Format handler
print(_Out,end_of_trace,_TI,N) ->
    N;
print(Out,Trace,_TI,N) ->
    do_print(Out,Trace,N),
    N+1.

do_print(Out,{trace_ts,P,call,{M,F,A},Ts},N) ->
    io:format(Out,
              "~w: ~w, ~w:~n"
              "Call      : ~w:~w/~w~n"
              "Arguments :~p~n~n",
              [N,Ts,P,M,F,length(A),A]);
do_print(Out,{trace_ts,P,return_from,{M,F,A},R,Ts},N) ->
    io:format(Out,
              "~w: ~w, ~w:~n"
              "Return from  : ~w:~w/~w~n"
              "Return value :~p~n~n",
              [N,Ts,P,M,F,A,R]).

為了區分使用此工具產生的追蹤日誌和其他日誌,在 tracer/2 中使用了選項 file。因此,日誌會被提取到名為 ttb_upload_debug_log-YYYYMMDD-HHMMSS 的目錄中

透過在啟動追蹤器時使用選項 handler,有關如何格式化檔案的資訊會儲存在追蹤資訊檔案 (.ti) 中。這並非必要,因為它可以在格式化時指定。但是,如果您想要使用 ttb:stop/1 中的選項 format 自動格式化追蹤日誌,這可能會很有用。此外,您不需要任何二進位日誌內容的知識,就可以按照預期的方式格式化它。如果在啟動追蹤器和格式化時都指定了選項 handler,則會使用格式化時指定的選項。

追蹤旗標 call 設定在所有處理序上。這表示任何使用命令 trc/1 啟動的函式都會在所有現有和新的處理序上追蹤。

針對遠端節點執行追蹤工具建構器

Observer 應用程式可能不一定在要追蹤的節點上可用(以下稱為「被追蹤節點」)。但是,只要滿足以下條件,追蹤工具建構器仍然可以從另一個節點執行(以下稱為「追蹤控制節點」)

  • Observer 應用程式在追蹤控制節點上可用。
  • Runtime_Tools 應用程式在追蹤控制節點和被追蹤節點上都可用。

如果要針對遠端節點使用追蹤工具建構器,強烈建議將追蹤控制節點啟動為隱藏。這樣,它就可以連接到被追蹤節點,而不會被它「看到」,也就是說,如果在被追蹤節點上呼叫 nodes/0 BIF,追蹤控制節點不會顯示。若要啟動隱藏節點,請將選項 -hidden 新增至 erl 命令,例如

% erl -sname trace_control -hidden

無磁碟節點

如果被追蹤節點是無磁碟的,則必須從具有磁碟存取的追蹤控制節點啟動 ttb,並且必須將選項 file 指定給函式 tracer/2,其值為 {local, File},例如

(trace_control@durin)1> ttb:tracer(mynode@diskless,
                                   {file,{local,{wrap,"mytrace"}}}).
{ok,[mynode@diskless]}

更多追蹤選項

設定追蹤時,也可以啟動以下功能

  • 時間限制追蹤
  • 過載保護
  • 自動恢復
  • dbg 模式

時間限制追蹤

有時啟用指定時間段的追蹤可能會很有幫助(例如,監控系統 24 小時或半秒)。可以使用選項 {timer, TimerSpec} 來完成此操作。如果 TimerSpec 的形式為 MSec,則在使用 ttb:stop/0 後,追蹤會在 MSec 毫秒後停止。如果提供了更多選項 (TimerSpec = {MSec, Opts}),則會改為使用 Opts 作為引數呼叫 ttb:stop/1

計時器會隨著 ttb:p/2 一起啟動,因此任何追蹤模式都必須預先設定。ttb:start_trace/4 總是在呼叫 ttb:p/2 之前設定所有模式。

以下範例顯示如何設定在 5 秒後自動停止和格式化的追蹤

(tiger@durin)1> ttb:start_trace([node()],
                                [{erlang, now,[]}],
                                {all, call},
                                [{timer, {5000, format}}]).

注意

由於網路和處理延遲,追蹤的期間是近似的。

過載保護

追蹤即時系統時,始終要特別注意不要因追蹤過於繁重而使節點過載。ttb 提供選項 overload 來解決此問題。

{overload, MSec, Module, Function} 指示 ttb 後端(Runtime_Tools 應用程式的一部分)每 MSec 毫秒執行一次過載檢查。如果檢查(名為 Module:Function(check))傳回 true,則會在選取的節點上停用追蹤。

在一個節點上啟動的過載保護不會影響其他節點,在其他節點上,追蹤會照常繼續。ttb:stop/0 會從所有用戶端提取資料,包括在啟動過載保護之前收集的所有資料。

注意

一旦在其中一個被追蹤節點中啟動過載保護,就不允許變更追蹤詳細資訊(使用 ttb:p/2ttb:tp/tpl...)。這是為了避免節點之間的追蹤設定不一致。

選項 overload 提供的 Module:Function 必須處理三個呼叫:initcheckstopinitstop 允許檢查所需的一些設定和解除設定。過載檢查模組可能如下所示

-module(overload).
-export([check/1]).

check(init) ->
    Pid = sophisticated_module:start(),
    put(pid, Pid);
check(check) ->
    get(pid) ! is_overloaded,
    receive
        Reply ->
            Reply
    after 5000 ->
            true
    end;
check(stop) ->
    get(pid) ! stop.

注意

check 始終由同一個處理序呼叫,因此 putget 是可行的。

自動恢復

節點可能會崩潰(可能是有錯誤的節點,因此會被追蹤)。使用 resume 在節點恢復時自動恢復對該節點的追蹤。當 Runtime_Tools 啟動時,失敗的節點會嘗試重新連接到追蹤控制節點。這表示 Runtime_Tools 必須包含在其他節點的啟動鏈中(如果沒有,您仍然可以透過手動啟動 Runtime_Tools 來恢復追蹤,也就是說,透過 RPC 呼叫)。

為了不遺失故障節點在當機前所儲存的資料,控制節點會在重新啟動追蹤之前嘗試擷取這些資料。此動作必須在允許的時間範圍內完成,否則會中止 (預設為 10 秒,但可以使用 {resume, MSec} 來變更)。以這種方式擷取的資料會與所有其他追蹤合併。

自動啟動功能需要在被追蹤的節點上儲存更多資料。預設情況下,資料會自動儲存到被追蹤節點目前工作目錄 (cwd) 中名為 "ttb_autostart.bin" 的檔案。使用者可以透過指定自己的模組來處理自動啟動資料的儲存和擷取 (即在無磁碟節點上),來變更此行為 (runtime_toolsttb_autostart_module 環境變數)。關於 API 的資訊,請參閱模組 ttb。以下範例顯示預設的處理器

-module(ttb_autostart).
-export([read_config/0,
         write_config/1,
         delete_config/0]).

-define(AUTOSTART_FILENAME, "ttb_autostart.bin").

delete_config() ->
    file:delete(?AUTOSTART_FILENAME).

read_config() ->
    case file:read_file(?AUTOSTART_FILENAME) of
        {ok, Data} -> {ok, binary_to_term(Data)};
        Error      -> Error
    end.

write_config(Data) ->
    file:write_file(?AUTOSTART_FILENAME, term_to_binary(Data)).

注意

請記住,檔案追蹤埠預設會緩衝資料。如果節點當機,追蹤訊息不會清除到二進位記錄檔中。如果故障風險很高,最好每隔一段時間自動清除緩衝區。將 {flush, MSec} 作為 ttb:tracer/2 的選項傳遞,會每 MSec 毫秒清除所有緩衝區。

dbg 模式

選項 {shell, ShellType} 可讓 ttb 的操作類似於 dbg。使用 {shell, true} 會在儲存所有追蹤訊息之前將其顯示在 Shell 中。{shell, only} 還會停用訊息儲存 (使工具的行為完全像 dbg)。只有 IP 追蹤埠 ({trace, {local, File}}) 才允許這樣做。

指令 ttb:tracer(dbg) 是純 dbg 模式 ({shell, only}) 的快捷方式。

追蹤資訊和檔案 .ti

除了追蹤記錄檔之外,當 Trace Tool Builder 啟動時,還會建立一個擴展名為 .ti 的檔案。這就是追蹤資訊檔案。它是一個二進位檔案,其中包含程序資訊、使用的追蹤標誌、所屬節點的名稱,以及使用函數 ttb:write_trace_info/2 寫入的所有資訊。.ti 檔案在停止追蹤時總是會與其他記錄檔一起擷取。

除了程序資訊之外,追蹤資訊檔案中的所有內容都會在格式化時傳遞到處理器函數。參數 TI{Key,ValueList} 元組的列表。金鑰 flagshandlerfilenode 用於 ttb 直接寫入的資訊。

可以透過呼叫 ttb:write_trace_info/2 將資訊新增到追蹤資訊檔案。請注意,ValueList 始終是一個列表,如果您使用相同的 Key 多次呼叫 write_trace_info/2,則每次都會使用新值擴展 ValueList

範例

ttb:write_trace_info(mykey,1)TI 中產生 {mykey,[1]} 的項目。另一個呼叫 ttb:write_trace_info(mykey,2) 會將此項目變更為 {mykey,[1,2]}

環繞記錄

如果您想限制追蹤記錄的大小,可以使用環繞記錄。它的運作方式幾乎與循環緩衝區相同。您可以指定二進位記錄的最大數量和每個記錄的最大大小。ttb 接著會在每次記錄達到最大大小時建立新的二進位記錄。當達到最大記錄數時,會在建立新記錄之前刪除最舊的記錄。

注意

ttb 產生的資料總大小可能大於環繞規格所建議的大小。如果被追蹤的節點重新啟動且啟用自動恢復,則始終會儲存舊的環繞記錄,並建立新的記錄。

環繞記錄可以逐個或一次全部格式化。請參閱格式化

格式化

格式化可以在停止 ttb 時自動完成 (請參閱 自動從所有節點收集和格式化記錄 一節),或透過呼叫函數 ttb:format/1,2 明確完成。

格式化是指讀取二進位記錄並以可讀的格式呈現。您可以使用 ttb 中的預設格式處理器,將每個追蹤訊息呈現為一行文字,或撰寫自己的處理器來對追蹤資訊進行更複雜的解釋。您也可以使用應用程式 ET 以圖形方式呈現追蹤記錄 (請參閱 使用事件追蹤器呈現追蹤記錄 一節)。

ttb:format/1,2 的第一個參數指定要格式化哪些二進位記錄。這通常是 ttb 在記錄擷取期間建立的目錄名稱。除非提供選項 disable_sort,否則不同檔案中的記錄始終會根據追蹤中的時間戳記排序。

ttb:format/2 的第二個參數是選項列表,如下所示

  • out - 指定寫入格式化文字的目的地。預設目的地為 standard_io,但也可以指定檔案名稱。

  • handler - 指定要使用的格式處理器。如果未指定此選項,則會使用啟動追蹤器時指定的選項 handler。如果啟動追蹤器時也未指定選項 handler,則會使用預設處理器,它會將每個追蹤訊息列印為文字行。

  • disable_sort - 表示不會根據時間戳記合併記錄,而是逐個處理檔案 (這可能會稍微快一點)。

格式處理器是一個接受四個參數的函數。會針對二進位記錄中的每個追蹤訊息呼叫此函數。一個只列印每個追蹤訊息的簡單範例如下

fun(Fd, Trace, _TraceInfo, State) ->
   io:format(Fd, "Trace: ~p~n", [Trace]),
   State
end.

在這裡,Fd 是目的地檔案的檔案描述符,或原子 standard_io_TraceInfo 包含來自追蹤資訊檔案的資訊 (請參閱追蹤資訊和檔案 .ti一節)。State 是格式處理器函數的狀態變數。變數 State 的初始值是在處理器選項中指定的,例如

ttb:format("tiger@durin-ttb", [{handler, {{Mod,Fun}, initial_state}}])
                                                     ^^^^^^^^^^^^^

可以使用另一個格式處理器來計算垃圾收集器所花費的時間

fun(_Fd,{trace_ts,P,gc_start,_Info,StartTs},_TraceInfo,State) ->
      [{P,StartTs}|State];
   (Fd,{trace_ts,P,gc_end,_Info,EndTs},_TraceInfo,State) ->
      {value,{P,StartTs}} = lists:keysearch(P,1,State),
      Time = diff(StartTs,EndTs),
      io:format("GC in process ~w: ~w milliseconds~n", [P,Time]),
      State -- [{P,StartTs}]
end

這個格式處理器的更精細版本是 Observer 應用程式 src 目錄中包含的 multitrace.erl 模組中的函數 handle_gc/4

追蹤訊息會作為第二個參數 (Trace) 傳遞。Trace 的可能值如下

  • erlang:trace/3 中描述的所有追蹤訊息
  • 如果使用 IP 追蹤器 (請參閱 dbg:trace_port/2),則為 {drop, N}
  • 當處理完所有追蹤訊息時,會收到一次 end_of_trace

透過提供格式處理器 ttb:get_et_handler(),您可以使用 ET 應用程式中的 et_viewer 以圖形方式呈現追蹤記錄 (請參閱使用事件追蹤器呈現追蹤記錄一節)。

您始終可以決定不格式化擷取目錄中包含的整個追蹤資料,而是分析單個檔案。若要這樣做,必須將單個檔案 (或檔案列表) 作為 format/1,2 的第一個參數傳遞。

環繞記錄可以逐個或一次全部格式化。若要格式化一組環繞記錄中的一個,請指定確切的檔案名稱。若要格式化整組環繞記錄,請指定名稱,並使用 * 取代環繞計數。

範例

開始追蹤

(tiger@durin)1> ttb:tracer(node(),{file,{wrap,"trace"}}).
{ok,[tiger@durin]}
(tiger@durin)2> ttb:p(...)
...

這會產生一組二進位記錄,例如

tiger@durin-trace.0.wrp
tiger@durin-trace.1.wrp
tiger@durin-trace.2.wrp
...

格式化整組記錄

1> ttb:format("tiger@durin-trace.*.wrp").
....
ok
2>

僅格式化第一個記錄

1> ttb:format("tiger@durin-trace.0.wrp").
....
ok
2>

若要合併來自兩個節點的所有環繞記錄

1> ttb:format(["tiger@durin-trace.*.wrp","lion@durin-trace.*.wrp"]).
....
ok
2>

使用事件追蹤器呈現追蹤記錄

有關事件追蹤器的詳細資訊,請參閱 ET 應用程式。

透過提供格式處理器 ttb:get_et_handler(),您可以使用 ET 應用程式中的 et_viewer 以圖形方式呈現追蹤記錄。ttb 提供可以從 et_viewer 視窗中的 *篩選器* 選單中選取的篩選器。篩選器會根據它們呈現的參與者類型 (也就是說,序列圖中每條垂直線代表的內容) 來命名。參與者之間的互動會顯示為兩條垂直線之間的紅色箭頭,參與者內部的活動會顯示為參與者線條右側的藍色文字。

processes 篩選器是唯一顯示追蹤記錄中所有追蹤訊息的篩選器。序列圖中的每條垂直線都代表一個程序。Erlang 訊息、產生和連結/取消連結是程序之間典型的互動。函式呼叫、排程和垃圾收集是程序內典型的活動。processes 是預設篩選器。

其餘篩選器僅顯示函式呼叫和函式傳回。所有其他追蹤訊息都會被捨棄。為了充分利用這些篩選器,et_viewer 必須知道每個函式的呼叫者和傳回時間。可以使用追蹤時的 callreturn_to 標誌來取得此資訊。請注意,標誌 return_to 僅適用於本機呼叫追蹤,也就是說,當追蹤模式設定為 ttb:tpl 時。

透過僅使用標誌 call 並在本機或全域函式呼叫上設定匹配規格,可以獲得相同的結果,如下所示

1> dbg:fun2ms(fun(_) -> return_trace(),message(caller()) end).
[{'_',[],[{return_trace},{message,{caller}}]}]

但是,必須謹慎執行此操作,因為匹配規格中的函式 {return_trace} 會破壞尾遞迴。

modules 過濾器會在循序圖中將每個模組顯示為一條垂直線。外部函式呼叫/返回會顯示為模組之間的互動,而內部函式呼叫/返回則會顯示為模組內的活動。

functions 過濾器會在循序圖中將每個函式顯示為一條垂直線。函式呼叫自身會顯示為函式內的活動,而所有其他函式呼叫則會顯示為函式之間的互動。

mods_and_procsfuncs_and_procs 過濾器分別等同於 modulesfunctions 過濾器,但每個模組或函式可以有多條垂直線,每個執行所在的處理程序各一條。

在以下範例中,會使用模組 foobar

-module(foo).
-export([start/0,go/0]).

start() ->
    spawn(?MODULE, go, []).

go() ->
    receive
        stop ->
            ok;
        go ->
            bar:f1(),
            go()
    end.
-module(bar).
-export([f1/0,f3/0]).
f1() ->
    f2(),
    ok.
f2() ->
    spawn(?MODULE,f3,[]).
f3() ->
    ok.

設定追蹤

(tiger@durin)1> %%First we retrieve the Pid to limit traced processes set
(tiger@durin)1> Pid = foo:start().
(tiger@durin)2> %%Now we set up tracing
(tiger@durin)2> ttb:tracer().
(tiger@durin)3> ttb:p(Pid, [call, return_to, procs, set_on_spawn]).
(tiger@durin)4> ttb:tpl(bar, []).
(tiger@durin)5> %%Invoke our test function and see output with et viewer
(tiger@durin)5> Pid ! go.
(tiger@durin)6> ttb:stop({format, {handler, ttb:get_et_handler()}}).

這會呈現類似以下的結果

Filter: "processes"

Filter: "mods_and_procs"

請注意,函式 ttb:start_trace/4 可以作為協助,如下所示

(tiger@durin)1> Pid = foo:start().
(tiger@durin)2> ttb:start_trace([node()],
                                [{bar,[]}],
                                {Pid, [call, return_to, procs, set_on_spawn]}
                                {handler, ttb:get_et_handler()}).
(tiger@durin)3> Pid ! go.
(tiger@durin)4> ttb:stop(format).

自動從所有節點收集和格式化日誌

預設情況下,ttb:stop/1 會從所有節點擷取追蹤日誌和追蹤資訊檔案。日誌會儲存在追蹤控制節點的工作目錄下,名為 ttb_upload-Filename-Timestamp 的新目錄中。可以透過為 ttb:stop/1 提供選項 nofetch 來停用擷取。使用者可以透過傳遞選項 {fetch_dir, Dir} 來指定擷取目錄。

如果為 ttb:stop/1 指定選項 format,則追蹤日誌會在停止追蹤後自動格式化。

歷史記錄和組態檔案

對於追蹤功能,可以使用 dbg 而不是 ttb,來設定處理程序的追蹤旗標和呼叫追蹤的追蹤模式,也就是函式 ptptplctpctplctpgttb 為這些函式僅新增以下兩件事

  • 所有呼叫都儲存在歷史記錄緩衝區中,可以重新呼叫並儲存在組態檔案中。這樣可以輕鬆設定相同的追蹤環境,例如,如果您想要比較兩個測試執行。也可以減少從 Erlang Shell 使用 ttb 時的輸入量。
  • 為最常見的匹配規格提供快捷方式(以便不強制您持續使用 dbg:fun2ms)。

使用 ttb:list_history/0 來檢視歷史記錄緩衝區的內容,並使用 ttb:run_history/1 重新執行其中一個項目。

歷史記錄緩衝區的主要目的是建立組態檔案的可能性。儲存在歷史記錄緩衝區中的任何函式都可以寫入組態檔案,並用於隨時透過單一函式呼叫建立特定的組態。

組態檔案是透過 ttb:write_config/2,3 建立或延伸的。組態檔案是二進位檔案,因此只能使用 ttb 提供的函式讀取和寫入。

可以透過呼叫 ttb:write_config(ConfigFile,all) 將歷史記錄緩衝區的完整內容寫入組態檔案。可以透過呼叫 ttb:write_config(ConfigFile,NumList) 來寫入從歷史記錄選取的項目,其中 NumList 是一個整數列表,指向要寫入的歷史記錄項目。此外,當呼叫 ttb:stop/0,1 時,歷史記錄緩衝區會始終傾印至 ttb_last_config

也可以透過呼叫函式 ttb:write_config(ConfigFile,ConfigList) 將使用者定義的項目寫入組態檔案,其中 ConfigList{Module,Function,Args} 的列表。

當呼叫 write_config/2 時,任何現有的檔案 ConfigFile 都會被刪除並建立新檔案。可以使用選項 append 將內容新增至現有組態檔案的末尾,例如 ttb:write_config(ConfigFile,What,[append])

範例

檢視歷史記錄緩衝區的內容

(tiger@durin)191> ttb:tracer().
{ok,[tiger@durin]}
(tiger@durin)192> ttb:p(self(),[garbage_collection,call]).
{ok,{[<0.1244.0>],[garbage_collection,call]}}
(tiger@durin)193> ttb:tp(ets,new,2,[]).
{ok,[{matched,1}]}
(tiger@durin)194> ttb:list_history().
[{1,{ttb,tracer,[tiger@durin,[]]}},
 {2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
 {3,{ttb,tp,[ets,new,2,[]]}}]

執行歷史記錄緩衝區中的項目

(tiger@durin)195> ttb:ctp(ets,new,2).
{ok,[{matched,1}]}
(tiger@durin)196> ttb:list_history().
[{1,{ttb,tracer,[tiger@durin,[]]}},
 {2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
 {3,{ttb,tp,[ets,new,2,[]]}},
 {4,{ttb,ctp,[ets,new,2]}}]
(tiger@durin)197> ttb:run_history(3).
ttb:tp(ets,new,2,[]) ->
{ok,[{matched,1}]}

將歷史記錄緩衝區的內容寫入組態檔案

(tiger@durin)198> ttb:write_config("myconfig",all).
ok
(tiger@durin)199> ttb:list_config("myconfig").
[{1,{ttb,tracer,[tiger@durin,[]]}},
 {2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
 {3,{ttb,tp,[ets,new,2,[]]}},
 {4,{ttb,ctp,[ets,new,2]}},
 {5,{ttb,tp,[ets,new,2,[]]}}]

延伸現有的組態

(tiger@durin)200> ttb:write_config("myconfig",[{ttb,tp,[ets,delete,1,[]]}],
[append]).
ok
(tiger@durin)201> ttb:list_config("myconfig").
[{1,{ttb,tracer,[tiger@durin,[]]}},
 {2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
 {3,{ttb,tp,[ets,new,2,[]]}},
 {4,{ttb,ctp,[ets,new,2]}},
 {5,{ttb,tp,[ets,new,2,[]]}},
 {6,{ttb,tp,[ets,delete,1,[]]}}]

在停止 Trace Tool Builder 後返回先前的組態

(tiger@durin)202> ttb:stop().
ok
(tiger@durin)203> ttb:run_config("myconfig").
ttb:tracer(tiger@durin,[]) ->
{ok,[tiger@durin]}

ttb:p(<0.1244.0>,[garbage_collection,call]) ->
{ok,{[<0.1244.0>],[garbage_collection,call]}}

ttb:tp(ets,new,2,[]) ->
{ok,[{matched,1}]}

ttb:ctp(ets,new,2) ->
{ok,[{matched,1}]}

ttb:tp(ets,new,2,[]) ->
{ok,[{matched,1}]}

ttb:tp(ets,delete,1,[]) ->
{ok,[{matched,1}]}

ok

將歷史記錄緩衝區中選取的項目寫入組態檔案

(tiger@durin)204> ttb:list_history().
[{1,{ttb,tracer,[tiger@durin,[]]}},
 {2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
 {3,{ttb,tp,[ets,new,2,[]]}},
 {4,{ttb,ctp,[ets,new,2]}},
 {5,{ttb,tp,[ets,new,2,[]]}},
 {6,{ttb,tp,[ets,delete,1,[]]}}]
(tiger@durin)205> ttb:write_config("myconfig",[1,2,3,6]).
ok
(tiger@durin)206> ttb:list_config("myconfig").
[{1,{ttb,tracer,[tiger@durin,[]]}},
 {2,{ttb,p,[<0.1244.0>,[garbage_collection,call]]}},
 {3,{ttb,tp,[ets,new,2,[]]}},
 {4,{ttb,tp,[ets,delete,1,[]]}}]
(tiger@durin)207>

循序追蹤

若要瞭解循序追蹤是什麼以及如何使用它,請參閱 seq_trace 的參考手冊。

Trace Tool Builder 提供的循序追蹤支援包括以下內容

  • 啟動系統追蹤器。當使用 ttb:tracer/0,1,2 啟動追蹤埠時,會自動完成此動作。
  • 建立會啟用循序追蹤的匹配規格。

啟動循序追蹤需要使用函式 ttb:tracer/0,1,2 啟動追蹤器。然後可以使用以下任一方式啟動循序追蹤

範例 1

在以下範例中,函式 dbg:get_tracer/0 會作為循序追蹤的觸發器

(tiger@durin)110> ttb:tracer().
{ok,[tiger@durin]}
(tiger@durin)111> ttb:p(self(),call).
{ok,{[<0.158.0>],[call]}}
(tiger@durin)112> ttb:tp(dbg,get_tracer,0,ttb:seq_trigger_ms(send)).
{ok,[{matched,1},{saved,1}]}
(tiger@durin)113> dbg:get_tracer(), seq_trace:reset_trace().
true
(tiger@durin)114> ttb:stop(format).
({<0.158.0>,{shell,evaluator,3},tiger@durin}) call dbg:get_tracer()
SeqTrace [0]: ({<0.158.0>,{shell,evaluator,3},tiger@durin})
{<0.237.0>,dbg,tiger@durin} ! {<0.158.0>,{get_tracer,tiger@durin}}
[Serial: {0,1}]
SeqTrace [0]: ({<0.237.0>,dbg,tiger@durin})
{<0.158.0>,{shell,evaluator,3},tiger@durin} ! {dbg,{ok,#Port<0.222>}}
[Serial: {1,2}]
ok
(tiger@durin)116>

範例 2

如果觸發函式不是直接從 Shell 呼叫,而是隱含在較大的系統中,則使用觸發器啟動循序追蹤會更有用。當從 Shell 呼叫函式時,直接啟動循序追蹤會更簡單,例如,如下所示

(tiger@durin)116> ttb:tracer().
{ok,[tiger@durin]}
(tiger@durin)117> seq_trace:set_token(send,true), dbg:get_tracer(),
seq_trace:reset_trace().
true
(tiger@durin)118> ttb:stop(format).
SeqTrace [0]: ({<0.158.0>,{shell,evaluator,3},tiger@durin})
{<0.246.0>,dbg,tiger@durin} ! {<0.158.0>,{get_tracer,tiger@durin}}
[Serial: {0,1}]
SeqTrace [0]: ({<0.246.0>,dbg,tiger@durin})
{<0.158.0>,{shell,evaluator,3},tiger@durin} ! {dbg,{ok,#Port<0.229>}}
[Serial: {1,2}]
ok
(tiger@durin)120>

在先前的兩個範例中,seq_trace:reset_trace/0 會在追蹤函式之後立即重設追蹤 Token,以避免因為 Erlang Shell 中的輸出而產生許多追蹤訊息。

除了 set_system_tracer/1 之外,在以 ttb:tracer/0,1,2 啟動追蹤埠後,可以使用模組 seq_trace 中的所有函式。

多用途追蹤工具

Observer 應用程式的 src 目錄中的模組 multitrace 提供了一個小型工具,其中有三個可能的追蹤設定。追蹤訊息會寫入二進位檔案,可以使用函式 multitrace:format/1,2 進行格式化

  • multitrace:debug(What) - 在所有處理程序上啟動呼叫追蹤,並追蹤指定的函式。使用的格式處理常式是 multitrace:handle_debug/4,它會列印每個呼叫和返回。What 必須是要追蹤的項目或項目列表,格式指定為 {Module,Function,Arity}{Module,Function} 或僅 Module

  • multitrace:gc(Procs) - 追蹤指定處理程序的記憶體回收。使用的格式處理常式是 multitrace:handle_gc/4,它會列印每次記憶體回收的開始、停止和花費的時間。

  • multitrace:schedule(Procs) - 追蹤指定處理程序的排入和排程。使用的格式處理常式是 multitrace:handle_schedule/4,它會列印每次排入和排程,以及處理程序、時間戳記和目前函式。它還會列印每個追蹤處理程序在排程中花費的總時間。