آشنایی با systrace

‫systrace ابزار اصلی برای تجزیه‌وتحلیل عملکرد دستگاه Android است. بااین‌حال، این ابزار درواقع پوششی برای ابزارهای دیگر است. این ابزار، بسته‌بندی سمت میزبان در اطراف atrace است، فایل اجرایی سمت دستگاه که ردیابی فضای کاربر را کنترل می‌کند و ftrace را راه‌اندازی می‌کند، و سازوکار ردیابی اصلی در هسته Linux است. systrace از atrace برای فعال کردن ردیابی استفاده می‌کند، سپس بافر ftrace را می‌خواند و همه آن‌ها را در یک بیننده HTML خودکفا بسته‌بندی می‌کند. (درحالی‌که هسته‌های جدیدتر از «فیلتر بسته برکلی بهبودیافته» (eBPF) لینوکس پشتیبانی می‌کنند، این صفحه به هسته ۳.۱۸ (بدون 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 و نمایشگر ترکیب شود، این امکان را به شما می‌دهد که از ورودی کاربر تا قاب نمایش‌داده‌شده روی صفحه ردیابی کنید. اندازه بافر را روی مقدار بزرگی تنظیم کنید تا رویدادها ازدست نروند (زیرا بدون بافر بزرگ، برخی‌از واحد‌های پردازش مرکزی پس‌از نقطه‌ای در ردیابی رویدادی ندارند).

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

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

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

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

برای بار کاری یکنواخت و دوره‌ای مثل TouchLatency، خط لوله واسط کاربر این توالی را دنبال می‌کند:

  1. EventThread در SurfaceFlinger رشته میانای کاربر برنامه را بیدار می‌کند و نشان می‌دهد که زمان پرداز جدید قاب فرا رسیده است.
  2. برنامه بااستفاده از منابع CPU و GPU، فریمی را در رشته میانای کاربری، RenderThread، و تکالیف HWUI پردازش می‌کند. این بخش عمده ظرفیت صرف‌شده برای رابط کاربری است.
  3. برنامه قاب پردازش‌شده را بااستفاده از یک چسب به SurfaceFlinger ارسال می‌کند، سپس SurfaceFlinger به حالت خواب می‌رود.
  4. «رشته رویداد» دوم در SurfaceFlinger،‏ SurfaceFlinger را برای راه‌اندازی ترکیب و خروجی نمایشگر بیدار می‌کند. اگر SurfaceFlinger تشخیص دهد که کاری برای انجام دادن وجود ندارد، دوباره به حالت خواب می‌رود.
  5. ‫SurfaceFlinger ترکیب را بااستفاده از «ترکیب‌کننده سخت‌افزاری» (HWC) یا «ترکیب‌کننده سخت‌افزاری ۲» (HWC2) یا GL انجام می‌دهد. ترکیب HWC و HWC2 سریع‌تر و کم‌مصرف‌تر است اما بسته به سیستم روی تراشه (SoC) محدودیت‌هایی دارد. این کار معمولاً حدود ۴ تا ۶ میلی‌ثانیه طول می‌کشد، اما می‌تواند با مرحله ۲ هم‌پوشانی داشته باشد زیرا برنامه‌های Android همیشه سه‌تایی بافر می‌شوند. (اگرچه برنامه‌ها همیشه بافر سه‌گانه دارند، ممکن است فقط یک قاب معلقه در SurfaceFlinger وجود داشته باشد که باعث می‌شود شبیه بافر دوگانه به‌نظر برسد.)
  6. ‫SurfaceFlinger برونداد نهایی را با درایور فروشنده به نمایشگر ارسال می‌کند و به حالت خواب می‌رود و منتظر بیدار شدن EventThread می‌ماند.

در اینجا نمونه‌ای از توالی قاب شروع‌شده از ۱۵۴۰۹ میلی‌ثانیه آورده شده است:

خط لوله واسط کاربر عادی با EventThread درحال اجرا

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

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

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

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

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

رشته میانای کاربر برای TouchLatency

شکل ۲. رشته میانای کاربر برای TouchLatency.

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

رشته میانای کاربر بیدار می‌شود، یک قاب را پرداز می‌کند، و آن را در صف مصرف SurfaceFlinger قرار می‌دهد

شکل ۳. رشته واسط کاربر بیدار می‌شود، چارچوبی را پرداز می‌کند، و آن را برای مصرف SurfaceFlinger در صف قرار می‌دهد.

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

تراکنش صحافی

شکل ۴. تراکنش پوشه.

شکل ۴ نشان می‌دهد که در ۱۵۴۲۳٫۶۵ میلی‌ثانیه Binder:6832_1 در SurfaceFlinger به‌دلیل tid 9579، که RenderThread مربوط به TouchLatency است، قابل اجرا می‌شود. همچنین می‌توانید queueBuffer را در هر دو طرف تراکنش پوشه ببینید.

درطول queueBuffer در سمت SurfaceFlinger، تعداد فریم‌های معلق از TouchLatency از ۱ به ۲ می‌رسد.

قاب‌های معلقه از ۱ به ۲ می‌رود

شکل ۵. قاب‌های معلقه از ۱ به ۲ می‌رود.

شکل ۵ بافربندی سه‌گانه را نشان می‌دهد که در آن دو قاب تکمیل‌شده وجود دارد و برنامه درحال شروع رندر کردن قاب سوم است. دلیل این موضوع این است که قبلاً چند فریم را حذف کرده‌ایم، بنابراین برنامه به‌جای یک فریم، دو فریم معلق را نگه می‌دارد تا از حذف فریم‌های بیشتر جلوگیری کند.

کمی بعد، رشته اصلی SurfaceFlinger توسط EventThread دوم بیدار می‌شود تا بتواند قاب معلقه قدیمی‌تر را در نمایشگر خروجی دهد:

رشته اصلی SurfaceFlinger توسط EventThread دوم بیدار می‌شود

شکل ۶. رشته اصلی SurfaceFlinger توسط EventThread دوم بیدار می‌شود.

ابتدا SurfaceFlinger بافر معلقه قدیمی‌تر را قفل می‌کند که باعث می‌شود تعداد بافر معلقه از ۲ به ۱ کاهش یابد:

‫SurfaceFlinger ابتدا به بافر معلقه قدیمی‌تر متصل می‌شود

شکل ۷. ‫SurfaceFlinger ابتدا به بافر معلقه قدیمی‌تر متصل می‌شود.

پس‌از قفل کردن بافر، SurfaceFlinger ترکیب را تنظیم می‌کند و قاب نهایی را به نمایشگر ارسال می‌کند. (برخی‌از این بخش‌ها به‌عنوان بخشی از mdss نقطه ردیابی فعال می‌شوند، بنابراین ممکن است در SoC شما وجود نداشته باشند.)

‫SurfaceFlinger ترکیب را تنظیم می‌کند و قاب نهایی را ارسال می‌کند

شکل ۸. SurfaceFlinger ترکیب‌بندی را راه‌اندازی می‌کند و قاب نهایی را ارسال می‌کند.

سپس، mdss_fb0 در واحد پردازش مرکزی ۰ بیدار می‌شود. ‫mdss_fb0 رشته هسته خط لوله نمایش برای برونداد کردن قاب پردازه‌شده در نمایشگر است. توجه داشته باشید که mdss_fb0 ردیف خود را در ردیابی دارد (برای مشاهده به پایین پیمایش کنید):

‫mdss_fb0 در واحد پردازش مرکزی ۰ بیدار می‌شود

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

‫mdss_fb0 بیدار می‌شود، کمی می‌دود، به خوابی بدون وقفه می‌رود، سپس دوباره بیدار می‌شود.

قاب غیرفعال

این مثال systrace مورداستفاده برای اشکال‌زدایی لرزش Pixel یا Pixel XL را شرح می‌دهد. برای دنبال کردن مثال، فایل فشرده ردیابی‌ها را بارگیری کنید (که شامل ردیابی‌های دیگری است که در این بخش به آن‌ها اشاره شده است)، فایل را از حالت فشرده خارج کنید، و فایل systrace_tutorial.html را در مرورگرتان باز کنید.

وقتی systrace را باز می‌کنید، چیزی شبیه به شکل زیر می‌بینید:

‫TouchLatency درحال اجرا روی Pixel XL با فعال بودن اکثر گزینه‌ها

شکل ۱۰. ‫TouchLatency درحال اجرا روی Pixel XL با فعال بودن اکثر گزینه‌ها.

در شکل ۱۰، اکثر گزینه‌ها فعال هستند، ازجمله نقاط ردیابی mdss و kgsl.

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

همبستگی FrameMissed با SurfaceFlinger

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

شکل ۱۱ قاب ازدست‌رفته‌ای را در ۱۵۵۹۸٫۲۹ میلی‌ثانیه نشان می‌دهد. SurfaceFlinger در فاصله VSync به‌طور خلاصه بیدار شد و بدون انجام هیچ کاری دوباره به خواب رفت، که یعنی SurfaceFlinger تشخیص داد که تلاش برای ارسال دوباره قاب به نمایشگر ارزش ندارد.

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

‫SurfaceFlinger بیدار می‌شود و بلافاصله به خواب می‌رود

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

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

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

mdss_fb0_retire fences

شکل ۱۳. حصارهای mdss_fb0_retire.

شکل ۱۳ قابی را نشان می‌دهد که به‌مدت ۳۳ میلی‌ثانیه نمایش داده شده است، نه ۱۶٫۷ میلی‌ثانیه که انتظار می‌رفت. در نیمه راه آن برش، آن قاب باید با قاب جدیدی جایگزین می‌شد اما نشد. قاب قبلی را ببینید و به‌دنبال چیزی بگردید.

قاب قبل‌از قاب خراب‌شده

شکل ۱۴. قاب قبلی قاب خراب.

شکل ۱۴ یک قاب ۱۴٫۴۸۲ میلی‌ثانیه‌ای را نشان می‌دهد. بخش دوفریمی شکسته ۳۳٫۶ میلی‌ثانیه بود، که تقریباً همان چیزی است که برای دو فریم انتظار می‌رود (در ۶۰ هرتز، ۱۶٫۷ میلی‌ثانیه در هر فریم، که نزدیک است). اما ۱۴٫۴۸۲ میلی‌ثانیه به ۱۶٫۷ میلی‌ثانیه نزدیک نیست، که نشان می‌دهد مشکلی در خط لوله نمایش وجود دارد.

برای تعیین اینکه چه کسی آن حصار را کنترل می‌کند، دقیقاً بررسی کنید که حصار کجا تمام می‌شود:

بررسی پایان نرده

شکل ۱۵. انتهای نرده را بررسی کنید.

صف کار حاوی __vsync_retire_work_handler است که وقتی حصار تغییر می‌کند درحال اجرا است. با نگاهی به منبع هسته، می‌توانید ببینید که این بخشی از درایور نمایشگر است. به‌نظر می‌رسد در مسیر بحرانی برای خط لوله نمایش قرار دارد، بنابراین باید در سریع‌ترین زمان ممکن اجرا شود. این رشته حدود ۷۰ میکروثانیه قابل‌اجرا است (تأخیر زمان‌بندی طولانی نیست)، اما یک صف کاری است و ممکن است به‌طور دقیق زمان‌بندی نشود.

قاب قبلی را بررسی کنید تا ببینید آیا در این امر نقش داشته است یا نه؛ گاهی اوقات لرزش می‌تواند در طول زمان جمع شود و در نهایت باعث ازدست رفتن مهلت شود:

قاب قبلی

شکل ۱۶. قاب قبلی.

خط اجراشدنی در kworker قابل‌مشاهده نیست زیرا بیننده آن را وقتی انتخاب می‌شود سفید می‌کند، اما آمارها داستان را می‌گویند: ۲٫۳ میلی‌ثانیه تأخیر زمان‌بندی برای بخشی از مسیر بحرانی خط لوله نمایشگر بد است. قبل‌از ادامه دادن، با انتقال این بخش از مسیر بحرانی خط لوله نمایش از صف کار (که به‌عنوان SCHED_OTHER رشته CFS اجرا می‌شود) به SCHED_FIFO kthread اختصاصی، تأخیر را برطرف کنید. این تابع به تضمین‌های زمان‌بندی نیاز دارد که صف‌های کاری نمی‌توانند (و قرار نیست) ارائه دهند.

مشخص نیست که این دلیل کندی باشد. به‌جز موارد آسان برای تشخیص مانند رقابت قفل هسته که باعث می‌شود رشته‌های حیاتی نمایش به خواب بروند، ردگیری‌ها معمولاً مشکل را مشخص نمی‌کنند. این لرزش ممکن است دلیل افت فریم باشد. زمان‌های حصار باید ۱۶٫۷ میلی‌ثانیه باشد، اما در قاب‌های منتهی به قاب حذف‌شده اصلاً به این مقدار نزدیک نیست. با توجه به اینکه خط لوله نمایشگر بسیار به‌هم پیوسته است، ممکن است لرزش در زمان‌بندی نرده منجر به حذف قاب شده باشد.

در این مثال، راه‌حل شامل تبدیل __vsync_retire_work_handler از صف کار به kthread اختصاصی است. این امر منجر به بهبود قابل‌توجه لرزش و کاهش پرش در آزمایش توپ جهنده می‌شود. ردگیری‌های بعدی زمان‌بندی‌های حصار را نشان می‌دهند که بسیار نزدیک به ۱۶٫۷ میلی‌ثانیه است.