Benutzerdefinierte Trace-Ereignisse im nativen Code

Android 6.0 (API-Level 23) und höher unterstützen eine native Tracing API, trace.h, um Trace-Ereignisse in den Systempuffer zu schreiben, den Sie dann mit Perfetto oder systrace. Häufige Anwendungsfälle für diese API sind die Beobachtung der Zeit die ein bestimmter Codeblock zum Ausführen benötigt und mit unerwünschtem Systemverhalten.

Hinweis:Auf Geräten und Emulatoren mit API-Level 27 und niedriger, falls vorhanden nicht genügend Arbeitsspeicher verfügbar ist oder der Speicher zu fragmentiert ist, folgende Nachricht: Atrace could not allocate enough memory to record a trace. Wenn dies passiert und Ihre Erfassung keinen vollständigen Datensatz hat, Hintergrundprozesse schließen oder das Gerät oder den Emulator neu starten.

Um benutzerdefinierte Ereignisse zu definieren, die im nativen Code Ihrer App oder Ihres Spiels auftreten, führen Sie die folgenden Schritte aus:

  1. Definieren Sie Funktionszeiger für die ATrace-Funktionen, mit denen Sie benutzerdefinierte Ereignisse in Ihrer App oder Ihrem Spiel erfassen, wie im folgenden Code gezeigt: 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. Lädt die ATrace-Symbole zur Laufzeit, wie im folgenden Code gezeigt: Snippet. Normalerweise führen Sie diesen Vorgang in einem Objektkonstruktor aus.

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

    Achtung : Aus Sicherheitsgründen sollten Sie Aufrufe von dlopen() nur in der Debug-Version deiner App oder deines Spiels.

    Hinweis : Wenn Sie Tracing-Unterstützung auch nach der Unter Android 4.3 (API-Level 18) können Sie mit JNI die Methoden in verwalteten Code um den im vorige Snippets.

  3. ATrace_beginSection() anrufen und ATrace_endSection() am Anfang bzw. Ende von Ihr benutzerdefiniertes Ereignis:

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

    Hinweis : Wenn Sie ATrace_beginSection() mehrere Mal, wird ATrace_endSection() nur am häufigsten beendet. mit der kürzlich aufgerufenen ATrace_beginSection()-Methode. Bei verschachtelten -Aufrufen, stellen Sie sicher, dass Sie jeden Aufruf ATrace_beginSection() mit einem Anruf bei ATrace_endSection().

    Außerdem können Sie ATrace_beginSection() nicht auf einem Gerät aufrufen. und beenden sie in einem anderen Thread. Beide Funktionen müssen vom selben Diskussions-Thread.

Praktische Tipps

Die folgenden Tipps sind optional, helfen Ihnen aber möglicherweise bei der Analyse Ihrer nativen Anzeigen. Code.

Gesamte Funktion verfolgen

Wenn Sie Ihren Aufrufstack oder das Timing von Funktionen instrumentieren, kann es nützlich sein, um ganze Funktionen nachzuverfolgen. Mit dem Makro ATRACE_CALL() können Sie Tracing-Typ einfacher einzurichten. Außerdem können Sie mit einem solchen Makro Erstellen von try- und catch-Blöcken für Fälle, in denen die Traced-Funktion eine Ausnahme auslösen oder return vorzeitig aufrufen.

Führen Sie die folgenden Schritte aus, um ein Makro für das Tracing einer gesamten Funktion zu erstellen:

  1. Definieren Sie das 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. Rufen Sie das Makro innerhalb der Funktion auf, die Sie verfolgen möchten:

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

Threads benennen

Sie können jeden Thread, in dem Ihre Ereignisse auftreten, benennen, wie gezeigt im folgenden Code-Snippet auf. Mit diesem Schritt können Sie die Threads leichter identifizieren die zu bestimmten Aktionen in deinem Spiel gehören.

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