trace.h

Emscripten 追蹤 API 提供了一些有用的功能,可以更好地了解應用程式內部的運作情況,特別是關於記憶體使用方面(傳統瀏覽器效能工具無法提供這些資訊)。

追蹤 API 可以與自訂收集伺服器(詳情請參閱執行伺服器)進行通訊,也可以與Google Web Tracing Framework 通訊。當與Google Web Tracing Framework 通訊時,會收集可用資料的子集。

用法

編譯器互動

當使用追蹤 API 時,您應該在每個編譯和連結階段將 --tracing 傳遞給 emcc。這會自動包含 library_trace.js 程式庫檔案,並設定預處理器標誌 __EMSCRIPTEN_TRACING__。如果您直接呼叫 clang 來建置您的 C/C++ 程式碼,則在建置程式碼時,您會想要傳遞 -D__EMSCRIPTEN_TRACING__。當未定義預處理器標誌 __EMSCRIPTEN_TRACING__ 時,追蹤 API 實作將由內嵌的空存根提供。

此外,由於啟用追蹤會修改 libc 實作中的 dlmalloc.c 實作,因此建議您在切換到使用追蹤 API 之前手動清除快取。如果您不這樣做,您將無法獲得完整的配置詳細資訊記錄。您可以使用這個 emcc 命令清除快取

emcc --clear-cache

初始化和拆解

若要初始化追蹤 API,您需要呼叫 emscripten_trace_configure()

emscripten_trace_configure("http://127.0.0.1:5000/", "MyApplication");

如果您只是要將追蹤 API 與 Google Web Tracing Framework 一起使用,則您可以改為呼叫 emscripten_trace_configure_for_google_wtf()

emscripten_trace_configure_for_google_wtf();

如果您有使用者名稱的概念,或有其他方式可以識別應用程式的特定使用者,則將其傳遞到追蹤 API 可以更容易在收集伺服器中識別工作階段

emscripten_trace_set_session_username(username);

若要在應用程式結束時關閉它,您只需呼叫 emscripten_trace_close()

emscripten_trace_close();

情境

情境是一種告訴追蹤 API 您應用程式的哪個部分正在執行的機制。情境實際上是作為目前情境的堆疊來維護的。

情境可能大到「執行物理」,小到「更新實體 X 上的動畫」。

情境堆疊的粒度取決於檢測其應用程式的團隊。某些應用程式可能會發現細粒度的情境更有用,而其他應用程式則更習慣使用較大的情境。

與其在每次追蹤呼叫時都取得堆疊追蹤,不如查看目前的情境堆疊並改為記錄它,這樣會便宜得多。

當伺服器完全實作情境時,它們也將用於追蹤每個情境中花費的時間(一種原始的分析機制),以及在情境處於活動狀態時配置和釋放的記憶體數量。這應該有助於清楚了解應用程式的哪些部分正在使用更多記憶體或產生大量波動(並可能導致堆積片段)。

記錄情境進入和離開很簡單

emscripten_trace_enter_context("Physics Update");
...
emscripten_trace_exit_context();

記錄幀或事件迴圈的開始和結束位置非常重要。這允許追蹤 API 執行有用的額外分析。

記錄事件迴圈的開始就像這樣簡單

emscripten_trace_record_frame_start();

記錄事件迴圈的結束也一樣容易

emscripten_trace_record_frame_end();

註解配置

應記錄每個配置和釋放操作。理想情況下,也應記錄資料類型名稱,但目前必須手動完成。

當使用 --tracing 和清除的快取建置時,Emscripten 建置的 libc 會自動記錄所有對 mallocreallocfree 的呼叫。

至於記錄資料類型名稱,在您配置記憶體後,您可以註解位址

emscripten_trace_annotate_address_type(model, "UI::Model");

此外,某些應用程式可能希望將額外儲存空間的大小與配置相關聯。這可以使用 emscripten_trace_associate_storage_size() 來完成

emscripten_trace_associate_storage_size(mesh, mesh->GetTotalMemoryUsage());

整體記憶體使用量

應定期向追蹤 API 報告整體堆積配置和記憶體使用量。

這可以使用 2 個呼叫來完成

emscripten_trace_report_memory_layout();
emscripten_trace_report_off_heap_data();

記錄訊息

可以透過 Emscripten 追蹤 API 記錄和記錄訊息。這些訊息可以同時具有通道和實際訊息。通道名稱將有助於在視覺化介面中對訊息進行分類和篩選。在記錄訊息時,您應避免在堆積上配置記憶體。

emscripten_trace_log_message("Application", "Started");

隨著時間的推移,視覺化介面將會改進,以協助您更好地將這些記錄訊息與其他視圖(例如記憶體隨時間的使用量)關聯起來。在分析記憶體使用行為模式時,記錄可能導致大量記憶體活動的內容(例如載入新模型或遊戲資產)的訊息非常有用。

任務

可以記錄和分析特定任務。任務通常是不重複的工作單元。它可能會由於部分執行非同步而暫停或遭到封鎖。

任務的範例是載入資產,通常涉及回呼鏈。

應用程式應追蹤任務 ID(整數)並確保它們是唯一的。

任務 ID 不需要傳遞給每個涉及任務的追蹤呼叫,因為大多數呼叫都是對目前任務進行操作。

可以使用以下命令啟動和停止任務

emscripten_trace_task_start(taskID, name);
emscripten_trace_task_end();

如果任務被暫停/封鎖,則可以使用以下命令記錄此情況

emscripten_trace_task_suspend("loading via HTTP");

當它恢復時

emscripten_trace_task_resume(taskID, "parsing");

通常需要將額外資料與目前任務關聯,以便稍後在檢查任務資料時使用。範例是已載入資產的 URL

emscripten_trace_task_associate_data("url", url);

回報錯誤

應用程式遇到的錯誤可以作為輔助服務回報給追蹤 API

emscripten_trace_report_error("Assertion failed: ...");

此功能作為 Emscripten 追蹤 API 未來發展方向的指示。

執行伺服器

設計注意事項

用戶端/伺服器設計

Emscripten 追蹤 API 從檢測的程式碼中收集資料,並將其傳輸到收集伺服器。伺服器還會執行資料分析,並提供用於檢視所收集資料的 Web 介面。

此用戶端/伺服器設計旨在讓工具在低階硬體(例如 32 位元 Windows 機器)上運行,而不會干擾瀏覽器,因為在這些硬體上記憶體可能很珍貴。

此設計還允許執行單一伺服器以從各種用戶端收集資料。

資料批次處理

資料會批次處理並以區塊形式傳送到伺服器,大約每秒一到兩次。這樣可以避免為每個要記錄的事件開啟與伺服器的新連線。

不要干擾堆積

當使用 Emscripten 追蹤 API 時,您應該注意不要執行會擾亂堆積的操作。例如,您不應該分配字串來傳遞給 emscripten_trace_log_message(),因為這會導致分配被追蹤,並可能干擾您嘗試分析的行為或結果。

基於這個原因,Emscripten 追蹤 API 也將其所有資料保存在 Emscripten 堆積之外,並且不會寫入 Emscripten 堆積。

函式

void emscripten_trace_configure(const char *collector_url, const char *application)
參數
  • collector_url (const char*) – 收集器伺服器的基本 URL。

  • application (const char*) – 被追蹤的應用程式名稱。

回傳類型

void

設定與收集器伺服器的連線。

這應該是在應用程式啟動後最先完成的事項之一。

在大多數情況下,collector_url 會是 http://127.0.0.1:5000/

void emscripten_trace_configure_for_google_wtf(void)
回傳類型

void

設定追蹤以與 Google Web Tracing Framework 通訊。

並非所有追蹤功能都可以在 Google WTF 工具中使用。(目前,僅支援上下文、日誌訊息和標記。)

void emscripten_trace_set_enabled(bool enabled)
參數
  • enabled (bool) – 是否啟用追蹤。

回傳類型

void

設定是否啟用追蹤。使用此選項停用追蹤可能會導致收集到不準確的記憶體使用數據。

void emscripten_trace_set_session_username(const char *username)
參數
  • username (const char*) – 執行應用程式的使用者名稱。

回傳類型

void

當多個人使用收集器伺服器,且您希望能夠透過時間戳記的工作階段 ID 以外的方式來識別個別工作階段時,這非常有用。

這可以在追蹤開始後設定,因此在使用者完成登入或驗證程序後設定此選項是沒有問題的。

void emscripten_trace_record_frame_start(void)
回傳類型

void

這應該在影格/事件迴圈的開始時呼叫。

目前的時戳與此資料相關聯。

伺服器使用此資料來追蹤影格時間(以及每秒影格數),並記錄在影格處理期間發生的記憶體操作。

void emscripten_trace_record_frame_end(void)
回傳類型

void

這應該在影格/事件迴圈的結尾呼叫。

目前的時戳與此資料相關聯。

伺服器使用此資料來停止累計記憶體操作以及影格的經過時間。

void emscripten_trace_log_message(const char *channel, const char *message)
參數
  • channel (const char*) – 發出的時間軸事件的類別。

  • message (const char*) – 發出的時間軸事件的描述。

回傳類型

void

記錄日誌訊息。這對於記錄已發生的事件或動作非常有用,這些事件或動作可能有利於與記憶體使用率或影格速率的變化進行關聯。

目前的時戳與此資料相關聯。

伺服器尚未充分利用此資料。未來將會改進。

void emscripten_trace_mark(const char *message)
參數
  • message (const char *) – 發出的標記名稱。

回傳類型

void

在時間軸中記錄標記。這主要用於 Google Web Tracing Framework

目前的時戳與此資料相關聯。

void emscripten_trace_report_error(const char *error)
參數
  • error (const char*) – 報告的錯誤訊息。

回傳類型

void

API 將取得目前的呼叫堆疊,並將其包含在給伺服器的報告中。

目前的時戳與此資料相關聯。

這可用於各種事項,包括擷取 JavaScript 和 Web Worker 錯誤,以及 C/C++ 程式碼中的失敗判斷提示或其他執行階段錯誤。

void emscripten_trace_record_allocation(const void *address, int32_t size)
參數
  • address (const void*) – 已分配的記憶體位址。

  • size (int32_t) – 分配的記憶體區塊大小。

回傳類型

void

必須針對每個記憶體分配呼叫此函數。執行此操作的最佳位置是在 Emscripten 中的 dlmalloc 實作中。

目前的時戳與此資料相關聯。

void emscripten_trace_record_reallocation(const void *old_address, const void *new_address, int32_t size)
參數
  • old_address (const void*) – 已重新分配的記憶體區塊的舊位址。

  • new_address (const void*) – 已重新分配的記憶體區塊的新位址。

  • size (int32_t) – 重新分配的記憶體區塊的新大小。

回傳類型

void

必須針對每個記憶體重新分配呼叫此函數。執行此操作的最佳位置是在 Emscripten 中的 dlmalloc 實作中。

目前的時戳與此資料相關聯。

void emscripten_trace_record_free(const void *address)
參數
  • address (const void*) – 要釋放的記憶體位址。

回傳類型

void

必須針對每個 free 操作呼叫此函數。執行此操作的最佳位置是在 Emscripten 中的 dlmalloc 實作中。

目前的時戳與此資料相關聯。

對於單一 free 操作,多次呼叫此函數也是不正確的。

void emscripten_trace_annotate_address_type(const void *address, const char *type)
參數
  • address (const void*) – 應註解的記憶體位址。

  • type (const char*) – 要分配的資料類型名稱。

回傳類型

void

使用儲存在該處的資料類型名稱來註解位址。伺服器使用此資訊來協助分析記憶體中的內容。

void emscripten_trace_associate_storage_size(const void *address, int32_t size)
參數
  • address (const void*) – 應註解的記憶體位址。

  • size (int32_t) – 與此分配相關聯的記憶體大小。

回傳類型

void

將額外儲存空間數量與此位址關聯。這不代表分配本身的大小,而是與此物件的大小相關聯時應考慮的相關記憶體。

此相關儲存空間在本質上是特定於應用程式的。

例如,當物件包含向量或字串時,您可能希望在分析記憶體使用率時知道此資訊,而這提供了一種讓伺服器了解該額外儲存空間的方式。

void emscripten_trace_report_memory_layout(void)
回傳類型

void

應該定期呼叫此函數以報告正常 Emscripten 堆積的使用情況。這提供堆疊和動態記憶體使用情況以及總記憶體大小的詳細資訊。

目前的時戳與此資料相關聯。

void emscripten_trace_report_off_heap_data(void)
回傳類型

void

應該定期呼叫此函數以報告不屬於正常 Emscripten 堆積的記憶體使用情況。目前用於報告 OpenAL 記憶體使用情況。

目前的時戳與此資料相關聯。

伺服器尚未顯示此資料。

void emscripten_trace_enter_context(const char *name)
參數
  • name (const char*) – 上下文名稱。

回傳類型

void

目前的時戳與此資料相關聯。

void emscripten_trace_exit_context(void)
回傳類型

void

目前的時戳與此資料相關聯。

void emscripten_trace_task_start(int task_id, const char *name);
參數
  • task_id (int) – 工作 ID

  • name (const char*) – 工作名稱

回傳類型

void

啟動工作。工作 ID 在應用程式的生命週期中應該是唯一的。它應該由應用程式管理/追蹤。

目前的時戳與此資料相關聯。

void emscripten_trace_task_associate_data(const char *key, const char *value);
參數
  • key (const char*) – 鍵

  • value (const char*) – 值

回傳類型

void

將鍵/值對與目前的工作相關聯。

void emscripten_trace_task_suspend(const char *explanation);
參數
  • explanation (const char*) – 工作暫停的原因。

回傳類型

void

目前的工作已暫停。

說明應指出工作暫停的原因,以便在檢視工作歷史記錄時可以使用此資訊。

目前的時戳與此資料相關聯。

void emscripten_trace_task_resume(int task_id, const char *explanation);
參數
  • task_id (int) – 工作 ID

  • explanation (const char*) – 工作恢復的原因。

回傳類型

void

恢復由 task_id 識別的工作,並將其設為目前的工作。

說明應指出工作恢復執行的目的,以便在檢視工作歷史記錄時可以使用此資訊。

目前的時戳與此資料相關聯。

void emscripten_trace_task_end(void);
回傳類型

void

目前的工作已結束。

目前的時戳與此資料相關聯。

void emscripten_trace_close(void)
回傳類型

void

應該在應用程式終止期間關閉此函數。這有助於確保將資料刷新到伺服器並終止追蹤程式碼。