Android 6.0 (livello API 23) e versioni successive supportano un'API di tracciamento nativa, trace.h
, per scrivere eventi di traccia nel buffer di sistema che potrai analizzare utilizzando Perfetto o systrace. I casi d'uso comuni per questa API includono l'osservazione del tempo impiegato da un particolare blocco di codice per essere eseguito e l'associazione di un blocco di codice a un comportamento indesiderato del sistema.
Nota: sui dispositivi e negli emulatori che eseguono il livello API 27 o precedente, se non è disponibile memoria sufficiente o se la memoria è troppo frammentata, verrà visualizzato il seguente messaggio: Atrace could not allocate enough memory to record a trace
.
Se si verifica questa situazione e l'acquisizione non include un set completo di dati, devi chiudere i processi in background o riavviare il dispositivo o l'emulatore.
Per definire gli eventi personalizzati che si verificano nel codice nativo all'interno dell'app o del gioco, procedi nel seguente modo:
Definisci i puntatori alle funzioni ATrace che utilizzi per acquisire eventi personalizzati nella tua app o nel tuo gioco, come mostrato nel seguente snippet di codice:
#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);
Carica i simboli ATrace in fase di runtime, come mostrato nel seguente snippet di codice. Di solito, questa procedura viene eseguita in un costruttore di oggetti.
// 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")); }
Attenzione : per motivi di sicurezza, includi le chiamate a
dlopen()
solo nella versione di debug dell'app o del gioco.Nota: per fornire supporto del tracciamento fino ad Android 4.3 (livello API 18), puoi utilizzare JNI per chiamare i metodi nel codice gestito intorno al codice mostrato nello snippet precedente.
Chiama
ATrace_beginSection()
eATrace_endSection()
rispettivamente all'inizio e alla fine del tuo evento personalizzato:#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();
Nota: se chiami più volte
ATrace_beginSection()
, la chiamata al numeroATrace_endSection()
termina solo il metodoATrace_beginSection()
chiamato più di recente. Quindi, per le chiamate nidificate, assicurati di abbinare correttamente ogni chiamata aATrace_beginSection()
con una chiamata aATrace_endSection()
.Inoltre, non puoi chiamare
ATrace_beginSection()
su un thread e terminarlo da un altro. Devi chiamare entrambe le funzioni dallo stesso thread.
Suggerimenti utili
I seguenti suggerimenti sono facoltativi, ma potrebbero semplificare l'analisi del codice nativo.
Traccia un'intera funzione
Durante l'implementazione dello stack di chiamate o della temporizzazione della funzione, potrebbe essere utile tracciare intere funzioni. Puoi utilizzare la macro ATRACE_CALL()
per semplificare la configurazione di questo tipo di tracciamento. Inoltre, una macro di questo tipo consente di saltare
la creazione di blocchi try
e catch
per i casi in cui la funzione tracciata potrebbe
generare un'eccezione o chiamare return
in anticipo.
Per creare una macro per tracciare un'intera funzione, procedi nel seguente modo:
Definisci la macro:
#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(); } };
Richiama la macro all'interno della funzione che vuoi tracciare:
void myExpensiveFunction() { ATRACE_CALL(); // Code that you want to trace. }
Assegna un nome ai thread
Puoi assegnare un nome a ogni thread in cui si verificano gli eventi, come mostrato nel seguente snippet di codice. Questo passaggio consente di identificare più facilmente i thread che appartengono ad azioni specifiche all'interno del gioco.
#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"); }
Consigliato per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Best practice per le prestazioni di SQLite
- Creare e misurare profili di riferimento senza Macrobenchmark