Google berkomitmen untuk mendorong terwujudnya keadilan ras bagi komunitas Kulit Hitam. Lihat caranya.

Menentukan peristiwa kustom

Pelacakan sistem hanya menunjukkan informasi terkait proses pada tingkat sistem, jadi terkadang sulit untuk mengetahui metode game atau aplikasi mana yang dieksekusi pada waktu tertentu yang terkait dengan peristiwa sistem.

Platform Android menyediakan API pelacakan yang dapat Anda gunakan untuk memberi label pada bagian kode tertentu. Jika Anda merekam aktivitas sistem baru dari versi "debug" aplikasi dan menyertakan opsi -a, seperti yang ditampilkan pada cuplikan berikut, peristiwa kustom ini muncul dalam laporan Systrace:

    python systrace.py -a com.example.myapp -b 16384 \
      -o my_systrace_report.html sched freq idle am wm gfx view binder_driver hal \
      dalvik camera input res
    

Opsi -a diperlukan untuk melacak aplikasi; tanpa opsi ini, metode aplikasi Anda tidak akan muncul di laporan Systrace.

Panduan ini menjelaskan cara menentukan peristiwa kustom dalam kode terkelola dan kode native.

Kode terkelola

Di Android 4.3 (API level 18) dan yang lebih tinggi, Anda dapat menggunakan class Trace di kode Anda untuk menentukan peristiwa kustom yang kemudian muncul di laporan Perfetto dan Systrace, seperti yang ditunjukkan dalam cuplikan kode berikut.

Catatan: Saat Anda memanggil beginSection() beberapa kali, memanggil endSection() hanya akan mengakhiri metode beginSection() yang terakhir dipanggil. Jadi, untuk panggilan bertingkat, seperti panggilan yang ada di cuplikan berikut, pastikan Anda mencocokkan dengan tepat setiap panggilan ke beginSection() dengan panggilan ke endSection().

Selain itu, Anda tidak dapat memanggil beginSection() di satu thread dan mengakhirinya di thread lainnya; jadi Anda harus memanggil kedua metode tersebut di thread yang sama.

Kotlin

    class MyAdapter : RecyclerView.Adapter<MyViewHolder>() {
        override fun onCreateViewHolder(parent: ViewGroup,
                viewType: Int): MyViewHolder {
            return try {
                Trace.beginSection("MyAdapter.onCreateViewHolder")
                MyViewHolder.newInstance(parent)
            } finally {
                // In try and catch statements, always call "endSection()" in a
                // "finally" block. That way, the method is invoked even when an
                // exception occurs.
                Trace.endSection()
            }
        }

        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            Trace.beginSection("MyAdapter.onBindViewHolder")
            try {
                try {
                    Trace.beginSection("MyAdapter.queryDatabase")
                    val rowItem = queryDatabase(position)
                    dataset.add(rowItem)
                } finally {
                    Trace.endSection()
                }
                holder.bind(dataset[position])
            } finally {
                Trace.endSection()
            }
        }
    }
    

Java

    public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            Trace.beginSection("MyAdapter.onCreateViewHolder");
            MyViewHolder myViewHolder;
            try {
                myViewHolder = MyViewHolder.newInstance(parent);
            } finally {
                // In try and catch statements, always call "endSection()" in a
                // "finally" block. That way, the method is invoked even when an
                // exception occurs.
                Trace.endSection();
            }
            return myViewHolder;
        }

       @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
            Trace.beginSection("MyAdapter.onBindViewHolder");
            try {
                try {
                    Trace.beginSection("MyAdapter.queryDatabase");
                    RowItem rowItem = queryDatabase(position);
                    dataset.add(rowItem);
                } finally {
                    Trace.endSection();
                }
                holder.bind(dataset.get(position));
            } finally {
                Trace.endSection();
            }
        }
    }
    

Kode Native

Android 6.0 (API level 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.

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 set data yang lengkap, tutup proses latar belakang, atau mulai 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 game, 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 konstruktor 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 (API level 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");
    }