Пользовательские события трассировки в машинном коде

Android 6.0 (уровень API 23) и более поздние версии поддерживают собственный API трассировки, trace.h , для записи событий трассировки в системный буфер, который затем можно проанализировать с помощью Perfetto или systrace. Общие случаи использования этого API включают наблюдение за временем, которое требуется для выполнения определенного блока кода, и связывание блока кода с нежелательным поведением системы.

Примечание. Если на устройствах и эмуляторах с уровнем API 27 и ниже недостаточно доступной памяти или память слишком фрагментирована, вы получите следующее сообщение: Atrace could not allocate enough memory to record a trace . Если это произошло и ваш захват не содержит полного набора данных, вам следует закрыть фоновые процессы или перезагрузить устройство или эмулятор.

Чтобы определить пользовательские события, которые происходят в собственном коде вашего приложения или игры, выполните следующие шаги:

  1. Определите указатели функций для функций 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);
    
  2. Загрузите символы 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 для вызова методов в управляемом коде на основе кода, показанного в предыдущем фрагменте кода.

  3. Вызовите 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() , чтобы упростить настройку этого типа трассировки. Кроме того, такой макрос позволяет пропустить создание блоков try и catch в тех случаях, когда отслеживаемая функция может выдать исключение или вызвать return раньше времени.

Чтобы создать макрос для трассировки всей функции, выполните следующие шаги:

  1. Определите макрос:

    #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. Вызовите макрос внутри функции, которую вы хотите отслеживать:

    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");
}
{% дословно %} {% дословно %} {% дословно %} {% дословно %}