Android 6.0 (API 級別 23) 以上版本支援原生追蹤 API trace.h
,可在系統緩衝區寫入追蹤紀錄事件,方便您使用 Perfetto 或 Systrace 進行分析。此 API 的常見用途包括觀察特定程式碼區塊的執行時間,以及將程式碼區塊與不理想的系統行為建立關聯。
注意:在搭載 API 級別 27 以下版本的裝置和模擬器上,如果可用的記憶體不足或記憶體不夠完整,系統會顯示以下訊息:Atrace could not allocate enough memory to record a trace
。在這種情況下,如果擷取的資料沒有完整的資料組合,您應關閉背景程序,或重新啟動裝置或模擬器。
如要定義在應用程式或遊戲的原生程式碼中發生的自訂事件,請完成下列步驟:
針對用於擷取應用程式或遊戲中自訂事件的 ATrace 函式,定義函式指標,如以下程式碼片段所示:
#include <android/trace.h> #include <dlfcn.h> void *(*ATrace_beginSection) (const char* sectionName); void *(*ATrace_endSection) (void); typedef void *(*fp_ATrace_beginSection) (const char* sectionName); typedef void *(*fp_ATrace_endSection) (void);
在執行階段載入 ATrace 符號,如以下程式碼片段所示。您通常可以在物件建構函式中執行此程序。
// Retrieve a handle to libandroid. void *lib = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL); // Access the native tracing functions. if (lib != NULL) { // Use dlsym() to prevent crashes on devices running Android 5.1 // (API level 22) or lower. ATrace_beginSection = reinterpret_cast<fp_ATrace_beginSection>( dlsym(lib, "ATrace_beginSection")); ATrace_endSection = reinterpret_cast<fp_ATrace_endSection>( dlsym(lib, "ATrace_endSection")); }
注意:基於安全考量,您只能在應用程式或遊戲的偵錯版本中加入對
dlopen()
的呼叫。注意:如要為 Android 4.3 (API 級別 18) 以上版本支援追蹤功能,可以使用 JNI 在上述程式碼片段所顯示的程式碼附近,呼叫受管理程式碼中的方法。
請在自訂事件開始和結束時,分別呼叫
ATrace_beginSection()
和ATrace_endSection()
:#include <android/trace.h> char *customEventName = new char[32]; sprintf(customEventName, "User tapped %s button", buttonName); ATrace_beginSection(customEventName); // Your app or game's response to the button being pressed. ATrace_endSection();
注意:如果多次呼叫
ATrace_beginSection()
,呼叫ATrace_endSection()
只會結束最近呼叫的ATrace_beginSection()
方法。因此,如要進行巢狀呼叫,請務必為每個ATrace_beginSection()
呼叫正確搭配一個ATrace_endSection()
呼叫。此外,您無法在一個執行緒上呼叫
ATrace_beginSection()
,然後從另一個執行緒結束該呼叫。您必須從相同的執行緒呼叫這兩個函式。
提升便利性的秘訣
您可以選用以下秘訣,或許能更輕鬆分析原生程式碼。
追蹤完整函式
檢測呼叫堆疊或函式時間時,追蹤完整函式可能會很有幫助。您可以使用 ATRACE_CALL()
巨集,更輕鬆設定此追蹤類型。此外,如果追蹤的函式可能會擲回例外狀況或提前呼叫 return
,此類巨集可讓您略過建立 try
和 catch
區塊的步驟。
如要建立可用於追蹤完整函式的巨集,請完成下列步驟:
定義巨集:
#define ATRACE_NAME(name) ScopedTrace ___tracer(name) // ATRACE_CALL is an ATRACE_NAME that uses the current function name. #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__) class ScopedTrace { public: inline ScopedTrace(const char *name) { ATrace_beginSection(name); } inline ~ScopedTrace() { ATrace_endSection(); } };
在要追蹤的函式內呼叫巨集:
void myExpensiveFunction() { ATRACE_CALL(); // Code that you want to trace. }
為執行緒命名
您可以為每個發生事件的執行緒命名,如以下程式碼片段所示。這個步驟可讓您更輕鬆辨別屬於遊戲中特定動作的執行緒。
#include <pthread.h> static void *render_scene(void *parm) { // Code for preparing your app or game's visual components. } static void *load_main_menu(void *parm) { // Code that executes your app or game's main logic. } void init_threads() { pthread_t render_thread, main_thread; pthread_create(&render_thread, NULL, render_scene, NULL); pthread_create(&main_thread, NULL, load_main_menu, NULL); pthread_setname_np(render_thread, "MyRenderer"); pthread_setname_np(main_thread, "MyMainMenu"); }
為您推薦
- 注意:系統會在 JavaScript 關閉時顯示連結文字
- 提升 SQLite 效能的最佳做法
- 不使用 Macrobenchmark 建立及評估基準設定檔