أحداث تتبُّع مخصصة في الرمز الأصلي

يتيح الإصدار Android 6.0 (المستوى 23 من واجهة برمجة التطبيقات) والإصدارات الأحدث استخدام واجهة برمجة تطبيقات أصلية للتتبّع، trace.h، لكتابة أحداث تتبُّع في المخزن المؤقت للنظام الذي يمكنك بعد ذلك تحليله باستخدام Perfetto أو systrace. وتشمل حالات الاستخدام الشائعة لواجهة برمجة التطبيقات هذه ملاحظة الوقت الذي تستغرقه كتلة معيّنة من الرموز في التنفيذ وربط كتلة من التعليمات البرمجية بسلوك النظام غير المرغوب فيه.

ملاحظة: على الأجهزة وأجهزة المحاكاة التي تستخدم المستوى 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() في إصدار تصحيح الأخطاء فقط من التطبيق أو اللعبة.

    ملاحظة: لتوفير دعم التتبُّع بشكل أكبر إلى الإصدار 4.3 من نظام التشغيل Android (المستوى 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");
}