Peristiwa rekaman aktivitas kustom dalam kode native

Android 6.0 (level API 23) dan yang lebih tinggi mendukung API pelacakan native, trace.h, untuk menulis peristiwa rekaman aktivitas ke buffer sistem yang kemudian dapat Anda analisis menggunakan Perfetto atau systrace. Contoh kasus penggunaan umum untuk API ini mencakup pengamatan waktu yang diperlukan blok kode tertentu untuk menjalankan dan mengaitkan blok kode dengan perilaku sistem yang tidak diinginkan.

Catatan: Pada perangkat dan emulator yang menjalankan API level 27 dan yang lebih rendah, jika memori yang tersedia tidak mencukupi atau memori terlalu terfragmen, Anda akan mendapatkan pesan berikut: Atrace could not allocate enough memory to record a trace. Jika hal ini terjadi dan rekaman Anda tidak memiliki kumpulan data yang lengkap, Anda harus menutup proses latar belakang atau memulai ulang perangkat atau emulator.

Untuk menentukan peristiwa kustom yang terjadi pada kode native dalam aplikasi atau game Anda, selesaikan langkah-langkah berikut:

  1. Tentukan pointer fungsi untuk fungsi ATrace yang Anda gunakan untuk mengambil peristiwa kustom dalam aplikasi atau game Anda, seperti yang ditampilkan pada cuplikan kode berikut:

    #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);
    
  2. Muat simbol ATrace saat waktu proses, seperti yang ditampilkan pada cuplikan kode berikut. Biasanya, Anda menjalankan proses ini pada pembuat objek.

    // 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"));
    }
    

    Perhatian: Untuk alasan keamanan, sertakan panggilan ke dlopen() hanya dalam versi debug aplikasi atau game Anda.

    Catatan: Untuk memberikan dukungan pelacakan hingga pada Android 4.3 (level API 18), Anda dapat menggunakan JNI guna memanggil metode dalam kode terkelola di sekitar kode yang ditampilkan pada cuplikan sebelumnya.

  3. Panggil ATrace_beginSection() dan ATrace_endSection() di awal dan akhir peristiwa kustom Anda:

    #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();
    

    Catatan: Saat Anda memanggil ATrace_beginSection() beberapa kali, memanggil ATrace_endSection() hanya akan mengakhiri metode ATrace_beginSection() yang terakhir dipanggil. Jadi, untuk panggilan bertingkat, pastikan Anda mencocokkan dengan tepat setiap panggilan ke ATrace_beginSection() dengan panggilan ke ATrace_endSection().

    Selain itu, Anda tidak dapat memanggil ATrace_beginSection() di satu thread dan mengakhirinya di thread lain. Anda harus memanggil kedua fungsi dari thread yang sama.

Tips untuk memudahkan

Tips berikut dapat Anda lakukan untuk memudahkan proses analisis kode native Anda.

Melacak seluruh fungsi

Saat melakukan instrumentasi stack panggilan atau pengaturan waktu fungsi, melacak seluruh fungsi mungkin akan bermanfaat. Anda dapat menggunakan makro ATRACE_CALL() untuk menjadikan jenis pelacakan ini lebih mudah disiapkan. Selain itu, makro semacam itu memungkinkan Anda melewati pembuatan blok try dan catch dalam berbagai kasus yang mana fungsi yang dilacak mungkin memberikan pengecualian atau panggilan return lebih awal.

Untuk membuat makro guna melacak seluruh fungsi, selesaikan langkah-langkah berikut:

  1. Tentukan makro:

    #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();
        }
    };
    
  2. Panggil makro pada fungsi yang ingin Anda lacak:

    void myExpensiveFunction() {
      ATRACE_CALL();
      // Code that you want to trace.
    }
    

Memberi nama thread Anda

Anda dapat memberi nama untuk setiap thread di tempat peristiwa terjadi, seperti yang diperlihatkan pada cuplikan kode berikut. Langkah ini memudahkan identifikasi thread yang termasuk dalam tindakan spesifik dalam game Anda.

#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");
}