درک Systrace

systrace ابزار اصلی برای تجزیه و تحلیل عملکرد دستگاه اندروید است. با این حال، این واقعا یک لفاف در اطراف ابزارهای دیگر است. این بسته بندی سمت میزبان در اطراف atrace است، فایل اجرایی سمت دستگاه که ردیابی فضای کاربر را کنترل می کند و ftrace را تنظیم می کند، و مکانیسم ردیابی اولیه در هسته لینوکس است. systrace از atrace برای فعال کردن ردیابی استفاده می‌کند، سپس بافر ftrace را می‌خواند و همه آن را در یک نمایشگر HTML مستقل می‌پیچد. (در حالی که هسته های جدیدتر از فیلتر بسته برکلی پیشرفته لینوکس (eBPF) پشتیبانی می کنند، مستندات زیر مربوط به هسته 3.18 (بدون eFPF) است، زیرا این همان چیزی است که در Pixel/Pixel XL استفاده شده است.)

systrace متعلق به تیم های Google Android و Google Chrome است و به عنوان بخشی از پروژه Catapult منبع باز است. علاوه بر systrace، Catapult شامل ابزارهای مفید دیگری نیز می شود. به عنوان مثال، ftrace دارای ویژگی‌های بیشتری نسبت به آنچه که مستقیماً توسط systrace یا atrace فعال می‌شود، دارد و دارای برخی عملکردهای پیشرفته است که برای اشکال‌زدایی مشکلات عملکرد حیاتی است. (این ویژگی ها نیاز به دسترسی ریشه و اغلب یک هسته جدید دارند.)

در حال اجرا systrace

هنگام اشکال زدایی جیتر در Pixel/Pixel XL، با دستور زیر شروع کنید:

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

هنگامی که با نقاط ردیابی اضافی مورد نیاز برای GPU و فعالیت خط لوله نمایش ترکیب می شود، این قابلیت به شما امکان ردیابی از ورودی کاربر تا فریم نمایش داده شده روی صفحه را می دهد. برای جلوگیری از از دست دادن رویدادها، اندازه بافر را روی چیزی بزرگ تنظیم کنید (زیرا بدون بافر بزرگ، برخی از CPU ها بعد از مدتی در ردیابی هیچ رویدادی ندارند).

هنگام عبور از systrace، به خاطر داشته باشید که هر رویداد توسط چیزی در CPU راه اندازی می شود .

از آنجایی که systrace در بالای ftrace ساخته شده است و ftrace بر روی CPU اجرا می شود، چیزی در CPU باید بافر ftrace را بنویسد که تغییرات سخت افزاری را ثبت کند. این بدان معناست که اگر کنجکاو هستید که چرا یک حصار نمایشگر تغییر حالت داده است، می‌توانید ببینید که دقیقاً در نقطه انتقال آن چه چیزی روی CPU اجرا می‌شود (چیزی در حال اجرا بر روی CPU باعث این تغییر در گزارش شده است). این مفهوم پایه و اساس تحلیل عملکرد با استفاده از systrace است.

مثال: قاب کاری

این مثال یک systrace را برای یک خط لوله معمولی UI توصیف می کند. برای دنبال کردن مثال، فایل زیپ Traces را دانلود کنید (که شامل سایر ردپای های اشاره شده در این بخش نیز می شود)، فایل را از حالت فشرده خارج کنید و فایل systrace_tutorial.html را در مرورگر خود باز کنید. هشدار داده شود که این systrace یک فایل بزرگ است. مگر اینکه از systrace در کارهای روزمره خود استفاده کنید، احتمالاً این ردیابی بسیار بزرگتر با اطلاعات بسیار بیشتر از آنچه قبلاً در یک ردیابی واحد دیده اید است.

برای یک بار کاری منظم و دوره ای مانند تاخیر لمسی، خط لوله UI شامل موارد زیر است:

  1. EventThread در SurfaceFlinger رشته رابط کاربری برنامه را بیدار می‌کند، و نشان می‌دهد که زمان ارائه یک فریم جدید فرا رسیده است.
  2. این برنامه با استفاده از منابع CPU و GPU یک فریم را در رشته UI، RenderThread و hwuiTasks ارائه می کند. این بخش عمده ظرفیتی است که برای رابط کاربری صرف شده است.
  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 میلی ثانیه شروع می شود:

خط لوله عادی UI با اجرای EventThread
شکل 1. خط لوله UI معمولی، EventThread در حال اجرا است

شکل 1 یک قاب معمولی است که توسط فریم های معمولی احاطه شده است، بنابراین نقطه شروع خوبی برای درک نحوه عملکرد خط لوله UI است. ردیف رشته رابط کاربری برای TouchLatency شامل رنگ های مختلف در زمان های مختلف است. نوارها حالت های مختلفی را برای نخ نشان می دهند:

  • خاکستری . خوابیدن.
  • آبی. قابل اجرا (می‌تواند اجرا شود، اما زمان‌بند هنوز آن را برای اجرا انتخاب نکرده است).
  • سبز. به طور فعال در حال اجرا (زمانبندی فکر می کند در حال اجرا است).
  • قرمز. خواب بدون وقفه (به طور کلی خوابیدن روی یک قفل در هسته). می تواند نشان دهنده بار I/O باشد. برای رفع اشکال مشکلات عملکرد بسیار مفید است.
  • نارنجی. خواب بدون وقفه به دلیل بار ورودی/خروجی.

برای مشاهده دلیل خواب بی وقفه (موجود از نقطه ردیابی sched_blocked_reason )، بخش خواب بی وقفه قرمز را انتخاب کنید.

در حالی که EventThread در حال اجرا است، رشته رابط کاربری برای TouchLatency قابل اجرا می شود. برای اینکه ببینید چه چیزی آن را بیدار کرده است، روی بخش آبی کلیک کنید.

رشته رابط کاربری برای تأخیر لمسی
شکل 2. رشته رابط کاربری برای تأخیر لمسی

شکل 2 نشان می دهد که رشته رابط کاربری TouchLatency توسط tid 6843 بیدار شده است که مربوط به EventThread است. رشته UI بیدار می شود، یک فریم را رندر می کند و آن را برای مصرف SurfaceFlinger در صف قرار می دهد.

رشته رابط کاربری بیدار می شود، یک فریم را رندر می کند و آن را در صف می گذارد تا SurfaceFlinger مصرف کند.
شکل 3. رشته UI بیدار می شود، یک فریم را رندر می کند و آن را برای مصرف SurfaceFlinger در صف قرار می دهد.

اگر تگ binder_driver در یک ردیابی فعال باشد، می توانید یک تراکنش بایندر را برای مشاهده اطلاعات مربوط به تمام فرآیندهای درگیر در آن تراکنش انتخاب کنید.

شکل 4. معامله بایندر

شکل 4 نشان می دهد که در 15423.65 میلی ثانیه Binder:6832_1 در SurfaceFlinger به دلیل tid 9579، که RenderThread TouchLatency است، قابل اجرا می شود. همچنین می توانید 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 روی CPU 0 بیدار می شود. mdss_fb0 رشته هسته خط لوله نمایشگر برای خروجی یک فریم رندر شده به نمایشگر است. ما می توانیم mdss_fb0 به عنوان ردیف خود در ردیابی ببینیم (برای مشاهده به پایین بروید).

mdss_fb0 روی CPU 0 بیدار می شود
شکل 9. mdss_fb0 روی CPU 0 بیدار می شود

mdss_fb0 بیدار می شود، به طور خلاصه اجرا می شود، وارد خواب بی وقفه می شود، سپس دوباره بیدار می شود.

مثال: قاب غیر کار

این مثال سیستمی را توصیف می کند که برای رفع اشکال Pixel/Pixel XL jitter استفاده می شود. برای دنبال کردن مثال، فایل زیپ Traces را دانلود کنید (که شامل سایر ردپاهای ذکر شده در این بخش است)، فایل را از حالت فشرده خارج کنید و فایل systrace_tutorial.html را در مرورگر خود باز کنید.

هنگامی که systrace را باز می کنید، چیزی شبیه به این خواهید دید:

TouchLatency روی Pixel XL اجرا می‌شود و اکثر گزینه‌ها فعال هستند
شکل 10. تأخیر لمسی در حال اجرا در Pixel XL (اکثر گزینه‌ها فعال هستند، از جمله نقاط ردیابی mdss و kgsl)

هنگامی که به دنبال jank هستید، ردیف FrameMissed را در زیر SurfaceFlinger بررسی کنید. FrameMissed یک بهبود کیفیت زندگی است که توسط HWC2 ارائه شده است. هنگام مشاهده systrace برای دستگاه‌های دیگر، اگر دستگاه از HWC2 استفاده نمی‌کند، ممکن است ردیف FrameMissed وجود نداشته باشد. در هر صورت، FrameMissed با SurfaceFlinger که یکی از زمان‌های اجرای بسیار منظم خود و تعداد بافرهای معلق بدون تغییر برای برنامه ( com.prefabulated.touchlatency ) را در vsync از دست داده است، مرتبط است.

همبستگی FrameMissed با SurfaceFlinger
شکل 11. همبستگی FrameMissed با SurfaceFlinger

شکل 11 یک فریم از دست رفته را در 15598.29&nbps;ms نشان می دهد. SurfaceFlinger برای مدت کوتاهی در فواصل vsync بیدار شد و بدون انجام کاری دوباره به خواب رفت، به این معنی که SurfaceFlinger تشخیص داد ارزش ارسال مجدد یک فریم به نمایشگر را ندارد. چرا؟

برای درک اینکه چگونه خط لوله برای این فریم خراب شد، ابتدا مثال فریم کاری بالا را مرور کنید تا ببینید چگونه یک خط لوله معمولی UI در systrace ظاهر می شود. پس از آماده شدن، به قاب از دست رفته برگردید و به سمت عقب حرکت کنید. توجه داشته باشید که SurfaceFlinger بیدار می شود و بلافاصله به خواب می رود. هنگام مشاهده تعداد فریم‌های معلق از TouchLatency، دو فریم وجود دارد (سرنخ خوبی برای کمک به فهمیدن اینکه چه خبر است).

SurfaceFlinger بیدار می شود و بلافاصله به خواب می رود
شکل 12. SurfaceFlinger بیدار می شود و بلافاصله به خواب می رود

از آنجایی که ما فریم هایی در SurfaceFlinger داریم، این یک مشکل برنامه نیست. علاوه بر این، SurfaceFlinger در زمان مناسب بیدار می شود، بنابراین مشکل SurfaceFlinger نیست. اگر SurfaceFlinger و برنامه هر دو عادی به نظر می رسند، احتمالاً مشکل درایور است.

از آنجایی که نقاط ردیابی mdss و sync فعال هستند، می‌توانیم اطلاعاتی درباره حصارها (مشترک‌شده بین درایور نمایشگر و SurfaceFlinger) که زمان ارسال فریم‌ها به نمایشگر را کنترل می‌کنند، دریافت کنیم. این حصارها در زیر mdss_fb0_retire فهرست شده‌اند، که نشان‌دهنده زمانی است که یک قاب روی صفحه نمایش است. این نرده ها به عنوان بخشی از دسته ردیابی sync ارائه می شوند. اینکه کدام حصارها با رویدادهای خاصی در SurfaceFlinger مطابقت دارند به SOC و پشته درایور شما بستگی دارد، بنابراین با فروشنده SOC خود کار کنید تا معنای دسته‌های حصار را در ردیابی‌های خود بفهمید.

mdss_fb0_حصارهای بازنشسته
شکل 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 اجرا می‌شود) به یک kthread اختصاصی SCHED_FIFO برطرف کنید. این تابع به ضمانت‌های زمان‌بندی نیاز دارد که صف‌های کاری نمی‌توانند (و قرار نیستند) ارائه کنند.

آیا این دلیل جنک است؟ سخت است که به طور قطعی بگوییم. خارج از مواردی که به راحتی قابل تشخیص هستند، مانند مناقشه قفل هسته که باعث خوابیدن رشته‌های حیاتی نمایشگر می‌شود، ردیابی‌ها معمولاً مشکل را مشخص نمی‌کنند. آیا این لرزش می تواند دلیل افت فریم باشد؟ کاملا. زمان‌های حصار باید 16.7 میلی‌ثانیه باشد، اما در فریم‌های منتهی به قاب افتاده اصلاً به آن نزدیک نیستند. با توجه به اتصال محکم خط لوله نمایشگر، این امکان وجود دارد که لرزش اطراف زمان بندی حصار منجر به افت قاب شود.

در این مثال، راه حل شامل تبدیل __vsync_retire_work_handler از یک صف کاری به یک kthread اختصاصی است. این منجر به بهبود قابل توجه لرزش و کاهش jank در تست توپ پرش شد. ردیابی های بعدی زمان بندی حصار را نشان می دهد که بسیار نزدیک به 16.7 میلی ثانیه شناور است.