فهم سيستراس

systrace هي الأداة الأساسية لتحليل أداء جهاز Android. ومع ذلك، فهو في الحقيقة غلاف حول أدوات أخرى. إنه غلاف من جانب المضيف حول atrace ، وهو الملف القابل للتنفيذ من جانب الجهاز الذي يتحكم في تتبع مساحة المستخدم ويقوم بإعداد ftrace ، وآلية التتبع الأساسية في Linux kernel. يستخدم systrace atrace لتمكين التتبع، ثم يقرأ المخزن المؤقت ftrace ويغلفه كله في عارض HTML قائم بذاته. (على الرغم من أن النوى الأحدث تدعم عامل تصفية حزم Berkeley المحسن لنظام التشغيل Linux (eBPF)، فإن الوثائق التالية تتعلق بالنواة 3.18 (بدون eFPF) لأن هذا هو ما تم استخدامه على Pixel/Pixel XL.)

تعود ملكية systrace إلى فريقي Google Android وGoogle Chrome، وهو مفتوح المصدر كجزء من مشروع Catapult . بالإضافة إلى systrace، يتضمن Catapult أدوات مساعدة أخرى مفيدة. على سبيل المثال، يحتوي ftrace على ميزات أكثر مما يمكن تمكينه مباشرة بواسطة systrace أو atrace ويحتوي على بعض الوظائف المتقدمة التي تعتبر ضرورية لتصحيح أخطاء الأداء. (تتطلب هذه الميزات الوصول إلى الجذر وغالبًا ما تتطلب نواة جديدة.)

تشغيل النظام

عند تصحيح أخطاء الارتعاش على Pixel/Pixel XL، ابدأ بالأمر التالي:

./systrace.py sched freq idle am wm gfx view sync binder_driver irq workq input -b 96000

عند دمجها مع نقاط التتبع الإضافية المطلوبة لوحدة معالجة الرسومات ونشاط خطوط أنابيب العرض، يمنحك هذا القدرة على التتبع من إدخال المستخدم إلى الإطار المعروض على الشاشة. اضبط حجم المخزن المؤقت على شيء كبير لتجنب فقدان الأحداث (لأنه بدون مخزن مؤقت كبير، لا تحتوي بعض وحدات المعالجة المركزية على أي أحداث بعد نقطة ما في التتبع).

عند المرور عبر systrace، ضع في اعتبارك أن كل حدث يتم تشغيله بواسطة شيء ما على وحدة المعالجة المركزية (CPU) .

نظرًا لأن systrace مبني على ftrace ويتم تشغيل ftrace على وحدة المعالجة المركزية (CPU)، يجب أن يكتب شيء ما على وحدة المعالجة المركزية المخزن المؤقت ftrace الذي يسجل تغييرات الأجهزة. هذا يعني أنه إذا كنت مهتمًا بمعرفة سبب تغيير حالة سياج العرض، فيمكنك رؤية ما كان يعمل على وحدة المعالجة المركزية عند النقطة المحددة للانتقال (هناك شيء يعمل على وحدة المعالجة المركزية أدى إلى حدوث هذا التغيير في السجل). هذا المفهوم هو أساس تحليل الأداء باستخدام systrace.

مثال: إطار العمل

يصف هذا المثال مسار النظام لخط أنابيب واجهة المستخدم العادي. لمتابعة المثال، قم بتنزيل ملف Zip الخاص بالآثار (الذي يتضمن أيضًا آثارًا أخرى مشار إليها في هذا القسم)، وقم بفك ضغط الملف، وافتح الملف systrace_tutorial.html في متصفحك. كن حذرًا من أن ملف systrace هذا كبير الحجم؛ ما لم تستخدم systrace في عملك اليومي، فمن المحتمل أن يكون هذا تتبعًا أكبر بكثير ويحتوي على معلومات أكثر بكثير مما رأيته في أي تتبع واحد من قبل.

للحصول على عبء عمل دوري ومتسق مثل TouchLatency، يحتوي مسار واجهة المستخدم على ما يلي:

  1. يعمل EventThread في SurfaceFlinger على تنشيط مؤشر ترابط واجهة مستخدم التطبيق، مما يشير إلى أن الوقت قد حان لعرض إطار جديد.
  2. يعرض التطبيق إطارًا في سلسلة واجهة المستخدم وRenderThread وhwuiTasks، باستخدام موارد وحدة المعالجة المركزية (CPU) ووحدة معالجة الرسومات (GPU). هذا هو الجزء الأكبر من السعة التي يتم إنفاقها على واجهة المستخدم.
  3. يرسل التطبيق الإطار المعروض إلى SurfaceFlinger باستخدام رابط، ثم ينتقل SurfaceFlinger إلى وضع السكون.
  4. يعمل EventThread الثاني في SurfaceFlinger على تنشيط SurfaceFlinger لتشغيل التركيب وعرض الإخراج. إذا قرر SurfaceFlinger أنه لا يوجد عمل يجب القيام به، فإنه يعود إلى وضع السكون.
  5. يتعامل SurfaceFlinger مع التكوين باستخدام Hardware Composer (HWC)/Hardware Composer 2 (HWC2) أو GL. يعد تكوين HWC/HWC2 أسرع وأقل طاقة ولكن له قيود اعتمادًا على النظام الموجود على الشريحة (SoC). يستغرق هذا عادةً ما بين 4 إلى 6 مللي ثانية، ولكن يمكن أن يتداخل مع الخطوة 2 نظرًا لأن تطبيقات Android تكون دائمًا مخزنة مؤقتًا ثلاث مرات. (على الرغم من أن التطبيقات يتم تخزينها مؤقتًا بشكل ثلاثي دائمًا، فقد يكون هناك إطار واحد فقط معلق في SurfaceFlinger، مما يجعله يبدو مطابقًا للتخزين المؤقت المزدوج.)
  6. يرسل SurfaceFlinger الإخراج النهائي لعرضه مع برنامج تشغيل البائع ويعود إلى وضع السكون، في انتظار تنبيه EventThread.

دعنا نسير عبر الإطار بدءًا من 15409 مللي ثانية:

خط أنابيب واجهة المستخدم العادي مع تشغيل EventThread
الشكل 1. مسار واجهة المستخدم العادي، تشغيل EventThread

الشكل 1 عبارة عن إطار عادي محاط بإطارات عادية، لذا فهو نقطة انطلاق جيدة لفهم كيفية عمل مسار واجهة المستخدم. يتضمن صف مؤشر ترابط واجهة المستخدم لـ TouchLatency ألوانًا مختلفة في أوقات مختلفة. تشير الأشرطة إلى حالات مختلفة للخيط:

  • رمادي . نائم.
  • أزرق. قابل للتشغيل (يمكن تشغيله، لكن المجدول لم يختره للتشغيل بعد).
  • أخضر. قيد التشغيل بنشاط (يعتقد المجدول أنه قيد التشغيل).
  • أحمر. النوم غير المنقطع (النوم عمومًا على قفل في النواة). يمكن أن يكون مؤشرا على تحميل الإدخال/الإخراج. مفيد للغاية لتصحيح مشكلات الأداء.
  • البرتقالي. النوم غير المنقطع بسبب تحميل الإدخال/الإخراج.

لعرض سبب السكون غير المنقطع (المتوفر من نقطة تتبع sched_blocked_reason )، حدد شريحة السكون غير المنقطع الحمراء.

أثناء تشغيل EventThread، يصبح مؤشر ترابط واجهة المستخدم لـ TouchLatency قابلاً للتشغيل. لمعرفة ما الذي أيقظه، انقر فوق القسم الأزرق.

موضوع واجهة المستخدم لـ TouchLatency
الشكل 2. موضوع واجهة المستخدم لـ TouchLatency

يوضح الشكل 2 أن مؤشر ترابط TouchLatency UI قد تم إيقاظه بواسطة tid 6843، والذي يتوافق مع EventThread. ينشط مؤشر ترابط واجهة المستخدم، ويعرض إطارًا، ويضعه في قائمة الانتظار حتى يستهلكه SurfaceFlinger.

ينشط مؤشر ترابط واجهة المستخدم، ويعرض إطارًا، ويضعه في قائمة الانتظار ليستهلكه SurfaceFlinger
الشكل 3. يتم تنشيط مؤشر ترابط واجهة المستخدم، ويعرض إطارًا، ويضعه في قائمة الانتظار ليستهلكه SurfaceFlinger

إذا تم تمكين علامة binder_driver في التتبع، فيمكنك تحديد معاملة Binder لعرض معلومات حول كافة العمليات المتضمنة في تلك المعاملة.

الشكل 4. معاملة الموثق

يوضح الشكل 4 أنه عند 15,423.65 مللي ثانية، يصبح Binder:6832_1 في SurfaceFlinger قابلاً للتشغيل بسبب tid 9579، وهو RenderThread الخاص بـ TouchLatency. يمكنك أيضًا رؤية queueBuffer على جانبي معاملة الموثق.

أثناء queueBuffer على جانب SurfaceFlinger، ينتقل عدد الإطارات المعلقة من TouchLatency من 1 إلى 2.

الإطارات المعلقة تنتقل من 1 إلى 2
الشكل 5. الإطارات المعلقة تنتقل من 1 إلى 2

يوضح الشكل 5 التخزين المؤقت الثلاثي، حيث يوجد إطاران مكتملان والتطبيق على وشك البدء في عرض إطار ثالث. وذلك لأننا أسقطنا بعض الإطارات بالفعل، لذا يحتفظ التطبيق بإطارين معلقين بدلاً من إطار واحد لمحاولة تجنب المزيد من الإطارات المسقطة.

بعد فترة وجيزة، يتم تنشيط مؤشر ترابط SurfaceFlinger الرئيسي بواسطة EventThread ثانٍ حتى يتمكن من إخراج الإطار المعلق الأقدم إلى الشاشة:

يتم إيقاظ مؤشر ترابط SurfaceFlinger الرئيسي بواسطة حدث EventThread ثانٍ
الشكل 6. يتم إيقاظ الخيط الرئيسي لـ SurfaceFlinger بواسطة EventThread ثانٍ

يقوم SurfaceFlinger أولاً بإغلاق المخزن المؤقت المعلق الأقدم، مما يؤدي إلى انخفاض عدد المخزن المؤقت المعلق من 2 إلى 1.

يتم تثبيت SurfaceFlinger أولاً على المخزن المؤقت الأقدم المعلق
الشكل 7. يتم تثبيت SurfaceFlinger أولاً على المخزن المؤقت القديم المعلق

بعد إغلاق المخزن المؤقت، يقوم SurfaceFlinger بإعداد التكوين وإرسال الإطار النهائي إلى الشاشة. (يتم تمكين بعض هذه الأقسام كجزء من نقطة تتبع mdss ، لذلك قد لا يتم تضمينها في شركة نفط الجنوب الخاصة بك.)

يقوم SurfaceFlinger بإعداد التكوين وتقديم الإطار النهائي
الشكل 8. يقوم SurfaceFlinger بإعداد التكوين وتقديم الإطار النهائي

بعد ذلك، يتم تنشيط mdss_fb0 على وحدة المعالجة المركزية 0. mdss_fb0 هو مؤشر ترابط kernel لخط أنابيب العرض لإخراج إطار معروض على الشاشة. يمكننا أن نرى mdss_fb0 كصف خاص به في التتبع (قم بالتمرير لأسفل للعرض).

يتم تنشيط mdss_fb0 على وحدة المعالجة المركزية 0
الشكل 9. يتم تنشيط mdss_fb0 على وحدة المعالجة المركزية 0

ينشط mdss_fb0 ، ويعمل لفترة وجيزة، ويدخل في وضع السكون غير المنقطع، ثم يستيقظ مرة أخرى.

مثال: إطار غير عامل

يصف هذا المثال systrace المستخدم لتصحيح عدم استقرار Pixel/Pixel XL. لمتابعة المثال، قم بتنزيل ملف التتبعات المضغوط (الذي يتضمن التتبعات الأخرى المشار إليها في هذا القسم)، وقم بفك ضغط الملف، وافتح الملف systrace_tutorial.html في متصفحك.

عندما تفتح نظام systrace، سترى شيئًا مثل هذا:

يعمل TouchLatency على Pixel XL مع تمكين معظم الخيارات
الشكل 10. تشغيل TouchLatency على Pixel XL (تم تمكين معظم الخيارات، بما في ذلك نقاط تتبع mdss وkgsl)

عند البحث عن النفايات، تحقق من صف FrameMissed ضمن SurfaceFlinger. FrameMissed هو تحسين لجودة الحياة يقدمه HWC2. عند عرض systrace للأجهزة الأخرى، قد لا يكون الصف FrameMissed موجودًا إذا كان الجهاز لا يستخدم HWC2. في كلتا الحالتين، يرتبط FrameMissed بفقدان SurfaceFlinger لأحد أوقات التشغيل المنتظمة للغاية وعدد المخزن المؤقت المعلق غير المتغير للتطبيق ( com.prefabulated.touchlatency ) في vsync.

العلاقة المفقودة مع SurfaceFlinger
الشكل 11. العلاقة بين FrameMissed وSurfaceFlinger

ويبين الشكل 11 إطارًا مفقودًا عند سرعة 15598.29 ثانية. استيقظ SurfaceFlinger لفترة وجيزة في الفاصل الزمني vsync وعاد إلى النوم دون القيام بأي عمل، مما يعني أن SurfaceFlinger قرر أنه لا يستحق محاولة إرسال إطار إلى الشاشة مرة أخرى. لماذا؟

لفهم كيفية تعطل المسار لهذا الإطار، راجع أولاً مثال إطار العمل أعلاه لترى كيف يظهر مسار واجهة المستخدم العادي في systrace. عندما تصبح جاهزًا، عد إلى الإطار المفقود واعمل للخلف. لاحظ أن SurfaceFlinger يستيقظ وينام على الفور. عند عرض عدد الإطارات المعلقة من TouchLatency، يوجد إطاران (دليل جيد للمساعدة في معرفة ما يحدث).

يستيقظ SurfaceFlinger وينام على الفور
الشكل 12. يستيقظ SurfaceFlinger وينام فورًا

نظرًا لأن لدينا إطارات في SurfaceFlinger، فهي ليست مشكلة في التطبيق. بالإضافة إلى ذلك، يتم تنشيط SurfaceFlinger في الوقت الصحيح، لذا فهي ليست مشكلة SurfaceFlinger. إذا كان كل من SurfaceFlinger والتطبيق يبدوا طبيعيين، فمن المحتمل أن تكون هناك مشكلة في برنامج التشغيل.

نظرًا لتمكين mdss ونقاط sync ، يمكننا الحصول على معلومات حول الأسوار (المشتركة بين برنامج تشغيل العرض وSurfaceFlinger) التي تتحكم في وقت إرسال الإطارات إلى الشاشة. تم إدراج هذه الأسوار ضمن mdss_fb0_retire ، والتي تشير إلى وقت ظهور الإطار على الشاشة. يتم توفير هذه الأسوار كجزء من فئة تتبع sync . تعتمد الأسوار التي تتوافق مع أحداث معينة في SurfaceFlinger على SOC ومجموعة برامج التشغيل الخاصة بك، لذا اعمل مع بائع SOC الخاص بك لفهم معنى فئات السياج في آثارك.

mdss_fb0_retire أسوار
الشكل 13. الأسوار mdss_fb0_retire

ويبين الشكل 13 الإطار الذي تم عرضه لمدة 33 مللي ثانية، وليس 16.7 مللي ثانية كما هو متوقع. في منتصف هذه الشريحة، كان من المفترض أن يتم استبدال هذا الإطار بآخر جديد، لكنه لم يحدث. عرض الإطار السابق والبحث عن أي شيء.

الإطار السابق للإطار المكسور
الشكل 14. الإطار السابق للإطار المكسور

ويبين الشكل 14 14.482 مللي ثانية للإطار. كان المقطع ذو الإطارين المكسور 33.6 مللي ثانية، وهو تقريبًا ما كنا نتوقعه لإطارين (نعرض عند 60 هرتز، 16.7 مللي ثانية لكل إطار، وهو قريب). لكن 14.482 مللي ثانية ليست قريبة من 16.7 مللي ثانية على الإطلاق، مما يشير إلى وجود خطأ ما في أنبوب العرض.

تحقق بالضبط من مكان انتهاء هذا السياج لتحديد ما يتحكم فيه.

التحقيق في نهاية السياج
الشكل 15. التحقق من نهاية السياج

تحتوي قائمة انتظار العمل على __vsync_retire_work_handler ، والذي يتم تشغيله عندما يتغير السياج. من خلال النظر إلى مصدر النواة، يمكنك أن ترى أنه جزء من برنامج تشغيل العرض. يبدو أنه على المسار الحرج لخط أنابيب العرض، لذلك يجب تشغيله بأسرع ما يمكن. يمكن تشغيلها لمدة 70 يومًا أو نحو ذلك (ليس تأخيرًا طويلًا في الجدولة)، ولكنها عبارة عن قائمة انتظار عمل وقد لا تتم جدولتها بدقة.

تحقق من الإطار السابق لتحديد ما إذا كان ذلك قد ساهم في ذلك؛ في بعض الأحيان يمكن أن يتراكم التوتر مع مرور الوقت ويؤدي في النهاية إلى تفويت الموعد النهائي.

الإطار السابق
الشكل 16. الإطار السابق

الخط القابل للتشغيل على kworker غير مرئي لأن العارض يحوله إلى اللون الأبيض عند تحديده، لكن الإحصائيات تحكي القصة: 2.3 مللي ثانية من تأخير الجدولة لجزء من المسار الحرج لخط أنابيب العرض أمر سيء . قبل المتابعة، قم بإصلاح التأخير عن طريق نقل هذا الجزء من المسار الحرج لخط أنابيب العرض من قائمة انتظار العمل (التي تعمل كمؤشر ترابط SCHED_OTHER CFS) إلى مؤشر SCHED_FIFO k مخصص. تحتاج هذه الوظيفة إلى ضمانات توقيت لا تستطيع قوائم انتظار العمل توفيرها (وليس المقصود منها).

فهل هذا هو سبب الهمزة؟ من الصعب أن نقول بشكل قاطع. باستثناء الحالات التي يسهل تشخيصها، مثل تنافس قفل kernel الذي يتسبب في سكون مؤشرات الترابط المهمة للعرض، فإن التتبعات عادةً لا تحدد المشكلة. هل يمكن أن يكون هذا الارتعاش هو سبب سقوط الإطار؟ قطعاً. يجب أن تكون أوقات السياج 16.7 مللي ثانية، ولكنها ليست قريبة من ذلك على الإطلاق في الإطارات المؤدية إلى الإطار المسقط. نظرًا لمدى اقتران خط العرض بإحكام، فمن المحتمل أن يؤدي الارتعاش المحيط بتوقيت السياج إلى سقوط الإطار.

في هذا المثال، تضمن الحل تحويل __vsync_retire_work_handler من قائمة انتظار العمل إلى kthread مخصص. أدى ذلك إلى تحسينات ملحوظة في الارتعاش وتقليل الخشونة في اختبار الكرة المرتدة. تظهر الآثار اللاحقة توقيت السياج الذي يحوم بالقرب من 16.7 مللي ثانية.