رویدادهای ردیابی سفارشی در کد بومی

‫Android نسخه ۶.۰ (سطح میانای برنامه‌سازی کاربردی ۲۳) و بالاتر از میانای برنامه‌سازی کاربردی ردیابی بومی، trace.h، پشتیبانی می‌کنند تا رویدادهای ردیابی را در میان‌گیر سیستم بنویسند که سپس می‌توانید بااستفاده از Perfetto یا systrace آن‌ها را تجزیه‌وتحلیل کنید. موارد استفاده رایج برای این میانای برنامه‌سازی کاربردی شامل مشاهده زمان اجرای یک بلوک کد خاص و مرتبط کردن یک بلوک کد با رفتار نامطلوب سیستم است.

توجه: در دستگاه‌ها و شبیه‌سازهایی که سطح API‏ ۲۷ و پایین‌تر را اجرا می‌کنند، اگر حافظه کافی دردسترس نباشد یا حافظه بیش‌ازحد تکه‌تکه شده باشد، پیام زیر را دریافت خواهید کرد: 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‏ ۱۸)، می‌توانید از 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");
}