O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Definir eventos personalizados

O rastreamento do sistema mostra informações sobre processos apenas no nível do sistema. Por isso, às vezes é difícil saber quais métodos do seu app ou jogo estavam sendo executados em determinado momento em relação aos eventos do sistema.

A plataforma Android oferece uma API de rastreamento que pode ser usada para etiquetar uma seção específica do código. Se você capturar um novo rastreamento do sistema da versão "debug" (de depuração) do seu app e incluir a opção -a, conforme mostrado no snippet a seguir, esses eventos personalizados aparecerão no relatório do 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
    

A opção -a é necessária para rastrear seu app. Sem ela, os métodos do app não serão exibidos no relatório do Systrace.

Este guia descreve como definir eventos personalizados tanto no código gerenciado quanto no código nativo.

Código gerenciado

No Android 4.3 (API de nível 18) e versões mais recentes, é possível usar a classe Trace no código para definir eventos personalizados que aparecerão nos relatórios do Perfetto e do Systrace, conforme mostrado no snippet de código a seguir.

Observação: quando beginSection() é chamado várias vezes, chamar endSection() encerra apenas o método beginSection() chamado mais recentemente. Por esse motivo, combine corretamente cada chamada de beginSection() com uma chamada de endSection() para chamadas aninhadas, como as do snippet a seguir.

Além disso, não é possível chamar beginSection() em uma linha de execução e depois encerrá-lo em outra. É preciso chamar os dois métodos na mesma linha de execução.

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

Código nativo

O Android 6.0 (API de nível 23) ou mais recentes é compatível com uma API de rastreamento nativa, trace.h, para registrar eventos de rastreamento no buffer do sistema que podem ser analisados usando o Perfetto ou o Systrace. Casos de uso comuns para essa API incluem observar o tempo que um determinado bloco de código leva para executar e associar um bloco com comportamento de sistema indesejado.

Em dispositivos e emuladores com a API de nível 27 e versões anteriores, se não houver memória suficiente disponível ou se a memória estiver muito fragmentada, você receberá a seguinte mensagem: Atrace could not allocate enough memory to record a trace. Se isso acontecer e a captura não tiver um conjunto completo de dados, feche os processos em segundo plano ou reinicie o dispositivo ou emulador.

Para definir eventos personalizados que ocorrem no código nativo do seu app ou jogo, siga as etapas a seguir:

  1. Defina os ponteiros de função para as funções ATrace usadas para capturar eventos personalizados no jogo, conforme apresentado no snippet de código a seguir:

        #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. Carregue os símbolos ATrace no tempo de execução, conforme mostrado no snippet de código a seguir. Normalmente, esse processo é executado em um construtor de objeto.

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

    Cuidado: por motivos de segurança, só inclua chamadas dlopen() na versão de depuração do seu app ou jogo.

    Observação: para oferecer compatibilidade de rastreamento até o Android 4.3 (API de nível 18), é possível usar o JNI para chamar os métodos no código gerenciado ao redor do código mostrado no snippet anterior.

  3. Chame ATrace_beginSection() e ATrace_endSection() no início e no fim do seu evento personalizado, respectivamente:

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

    Observação: quando ATrace_beginSection() é chamado várias vezes, chamar ATrace_endSection() encerra apenas o método ATrace_beginSection() chamado mais recentemente. Por esse motivo, combine corretamente cada chamada de ATrace_beginSection() com uma chamada de ATrace_endSection() para chamadas aninhadas, como as do snippet a seguir.

    Além disso, não é possível chamar ATrace_beginSection() em uma linha de execução e depois encerrá-la em outra. É preciso chamar as duas funções na mesma linha de execução.

Dicas para sua conveniência

As dicas a seguir são opcionais, mas podem facilitar a análise do seu código nativo.

Rastrear uma função inteira

Ao instrumentar a pilha de chamadas ou o tempo da função, pode ser útil rastrear funções inteiras. É possível usar a macro ATRACE_CALL() para facilitar esse tipo de rastreamento. Além disso, essa macro permite pular a criação de blocos try e catch para casos em que a função rastreada pode gerar uma exceção ou chamar return antecipadamente.

Para criar uma macro para rastrear uma função inteira, siga estas etapas:

  1. Defina a 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();
            }
        };
        
  2. Chame a macro na função que você quer rastrear:

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

Nomeie suas linhas de execução

Você pode dar um nome para cada linha de execução em que os eventos ocorrem, conforme demonstrado no snippet de código a seguir. Esta etapa facilita a identificação das linhas de execução que pertencem a ações específicas no seu jogo.

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