Niestandardowe zdarzenia śledzenia w kodzie natywnym

Android 6.0 (poziom interfejsu API 23) i nowsze wersje obsługują natywny interfejs API śledzenia trace.h, do zapisywania zdarzeń śledzenia w buforze systemowym, które można następnie przeanalizować za pomocą Perfetto lub systrace. Typowe przypadki użycia tego interfejsu API obejmują obserwację czasu konkretnego bloku kodu do wykonania, wiążącego blok kodu, przy niepożądanemu działaniu systemu.

Uwaga: na urządzeniach i emulatorach z interfejsem API na poziomie 27 lub niższym, o ile jest dostępny za mało dostępnej pamięci lub jest ona zbyt pofragmentowana, ta wiadomość: Atrace could not allocate enough memory to record a trace. Jeśli tak się stanie i przechwytywanie nie ma pełnego zestawu danych, powinien zamknąć procesy w tle albo ponownie uruchomić urządzenie lub emulator.

Aby zdefiniować zdarzenia niestandardowe występujące w kodzie natywnym w aplikacji lub grze: wykonaj te czynności:

  1. Zdefiniuj wskaźniki funkcji dla funkcji ATrace, których używasz do rejestruje zdarzenia niestandardowe w aplikacji lub grze, zgodnie z tym kodem snippet:

    #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. Wczytywanie symboli ATrace w czasie działania, jak pokazano w tym kodzie . Zwykle ten proces wykonuje się w konstruktorze obiektów.

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

    Uwaga: ze względów bezpieczeństwa uwzględnij połączenia z dlopen() tylko w wersji do debugowania aplikacji lub gry.

    Uwaga: aby zapewnić pomoc w śledzeniu, od Androida 4.3 (poziom interfejsu API 18), możesz używać JNI do wywoływania metod kodu zarządzanego wokół kodu widocznego w sekcji poprzedzający fragment kodu.

  3. Zadzwoń pod numer ATrace_beginSection() i ATrace_endSection() odpowiednio na początku i na końcu, Twoje zdarzenie niestandardowe:

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

    Uwaga: gdy dzwonisz do: ATrace_beginSection() pod wiele dzwonienia pod numer ATrace_endSection() kończy się najczęściej nazywanej ostatnio metodą ATrace_beginSection(). Zagnieżdżony upewnij się, że każde wywołanie jest odpowiednio dopasowane do ATrace_beginSection() z połączeniem do ATrace_endSection()

    Nie możesz też zadzwonić do ATrace_beginSection() na jeden i zakończyć od innego. Obie funkcje musisz wywoływać z tego samego w wątku.

Wskazówki wygodne

Podane niżej wskazówki są opcjonalne, ale mogą ułatwić analizę reklam natywnych w kodzie.

Śledzenie całej funkcji

Może Ci się to przydać przy instrumentowaniu stosu wywołań lub czasu funkcji do śledzenia całych funkcji. Aby to zrobić, możesz użyć makra ATRACE_CALL() jest łatwiejszy do skonfigurowania. Poza tym takie makro pozwala pominąć tworząc bloki try i catch w przypadkach, gdy funkcja śledzona może zgłosić wyjątek lub wywołać return wcześniej.

Aby utworzyć makro do śledzenia całej funkcji, wykonaj te czynności:

  1. Zdefiniuj 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. Wywołaj makro w ramach funkcji, którą chcesz śledzić:

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

Nadawanie nazw wątkom

Każdy wątek, w którym występują zdarzenia, możesz nazwać zgodnie z przykładem w tym fragmencie kodu. Ten krok ułatwia rozpoznawanie wątków które należą do konkretnych działań w grze.

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