فهم Systrace

systrace هي الأداة الأساسية لتحليل أداء جهاز Android. ومع ذلك ، فهو حقًا غلاف حول الأدوات الأخرى. إنه غلاف جانب المضيف حول atrace ، وهو الملف التنفيذي من جانب الجهاز الذي يتحكم في تتبع مساحة المستخدمين وإعداد ftrace ، وآلية التتبع الأساسية في Linux kernel. يستخدم systrace atrace لتمكين التتبع ، ثم يقرأ المخزن المؤقت ftrace ويلف كل شيء في عارض HTML مستقل. (على الرغم من أن النوى الأحدث تدعم Linux Enhanced Berkeley Packet Filter (eBPF) ، فإن الوثائق التالية تتعلق بـ 3.18 kernel (بدون 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 ، ضع في اعتبارك أن كل حدث يتم تشغيله بواسطة شيء موجود على وحدة المعالجة المركزية .

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

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

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

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

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

لننتقل عبر الإطار الذي يبدأ عند 15409 مللي ثانية:

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

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

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

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

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

مؤشر ترابط واجهة المستخدم لـ TouchLatency
الشكل 2. مؤشر ترابط واجهة المستخدم لـ TouchLatency

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

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

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

الشكل 4. صفقة Binder

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

أثناء 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 ، لذلك قد لا يتم تضمينها في SoC الخاص بك.)

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

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

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

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

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

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

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

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

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

الإطار ارتباط مفقود مع SurfaceFlinger
الشكل 11. الإطار الارتباط المفقود مع SurfaceFlinger

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

لفهم كيفية تعطل خط الأنابيب لهذا الإطار ، راجع أولاً مثال إطار العمل أعلاه لترى كيف يظهر خط أنابيب عادي لواجهة المستخدم في النظام. عندما تصبح جاهزًا ، عد إلى الإطار المفقود واعمل للخلف. لاحظ أن 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 ، والذي يتم تشغيله عند تغيير السياج. بالنظر إلى مصدر kernel ، يمكنك أن ترى أنه جزء من برنامج تشغيل العرض. يبدو أنه على المسار الحرج لخط أنابيب العرض ، لذا يجب تشغيله بأسرع ما يمكن. إنه قابل للتشغيل لـ 70 شخصًا أو نحو ذلك (ليس تأخيرًا طويلاً للجدولة) ، ولكنه عبارة عن قائمة انتظار عمل وقد لا يتم جدولتها بدقة.

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

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

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

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

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