Всем день добрый! Это статья о том, как профайлить Unity игры на Android с Android Studio. Я расскажу о том, как настроить Android Studio и получить максимальное кол-во данных. Вопросы анализа и выводов на основе полученного результата находятся вне рамок данной статьи.
Для полноценного профилирования приложения вам потребуется телефон с Android 8 или новее (API 27). По опыту, профилирование с Android-ами более старых версий несёт больше приключений, чем пользы. По этой причине я рекомендую обзавестись полной линейкой Nexus устройств, так как они имеют старое железо и последние обновления Android-а.
Для получения результата, вам придётся настроить Unity проект определённым образом.
![image](https://habrastorage.org/webt/av/tr/ju/avtrjuzmsq9xk8egl88e_osxbz4.jpeg)
![image](https://habrastorage.org/webt/-7/hz/ho/-7hzhozfm55alhksgs0jozpm488.jpeg)
После того, как вы установили все настройки, соберите Unity проект. Вы должны получить папку с Gradle проектом.
![image](https://habrastorage.org/webt/vt/qq/fi/vtqqfixujd8ntg_ipoycbp9exwc.jpeg)
Unity по умолчанию собирает проект из расчёта, что вы планируете собирать из него финальный APK. Потому вся отладочная информацию из него удалена, но к счастью её можно вернуть. Для этого вам нужно подменить libil2cpp.so и libunity.so на версии с отладочной информацией.
Это файл, который содержит весь C++ код, который IL2CPP сгенерировал из вашего C# кода. Unity генерирует всю необходимую для профилирования информацию, но для оптимизации размера APK, в процессе сборки вырезает её. Для того, чтобы вернуть её:
Это файл, который содержит low-level часть Unity Player. Так как мы делаем Release сборку, то Unity положила вам в проект файл без отладочной информации. Вам нужно подменить libunity.so на файл с символами.
Теперь вы можете начать профилировать в Android Studio, достаточно нажать кнопку запуска профайлера.
![image](https://habrastorage.org/webt/le/b3/yi/leb3yimardolvn0inirdf0hh-ak.jpeg)
Android Studio запустит приложение и начнётся сессия профилирования
![image](https://habrastorage.org/webt/q7/jv/na/q7jvnadenwr-9ixtxu-ac7l0exe.jpeg)
По умолчанию, Android Studio показывается графики, но не производит сэмплирование данных. Для запуска процесса, вам нужно кликнуть на треке CPU, чтобы профайлер переключится на вид CPU. При этом сверху окна появится дропдаун и кнопка ‘Record’.
![image](https://habrastorage.org/webt/_q/1t/qi/_q1tqikjnm-9vqltl3zvwh8t1am.jpeg)
Выберите Sampled ‘Native’ (В Android Studio 3.3 — C/C++ Native), и нажмите кнопку ‘Record’.
Так как запись производится на диск устройства, по опыту лучше не записывать более 5-8 секунд, многие устройства будут фейлится и на меньшем кол-ве данных (список проверенных устройств смотрите в конце статьи).
Для получения результата нажмите ‘Stop’ и потом красный квадрат, чтобы прервать сессию. Сложно понять задумку авторов, но если вы не остановите запись полностью, то профайлер не всегда начинает анализ полученных данных и ваш отрезок с данным уедет в далёкие дали.
![image](https://habrastorage.org/webt/nc/s6/wb/ncs6wbzxzmnl2ekezsbeikdalto.jpeg)
После этого остаётся только немного подождать, через 30-50 секунд профайлер выдаст вам результат. Если всё настроено верно, вы получите capture со всем именами функций
![image](https://habrastorage.org/webt/ft/ws/9a/ftws9av2ird_wbbvppc3af9smey.jpeg)
![image](https://habrastorage.org/webt/f_/it/lp/f_itlpp-dlvolgu5pb-ze7rz4oe.jpeg)
Мы для экспериментов использовали следующие устройства:
Все устройства были зарутованы, для Huawei Nexus 6P мы собрали ядро и AOSP. По результату самыми беспроблемными и простыми для работы оказались Google и Sony. У Sony есть отличный сайт для разработчиков — Open Devices. Huawei больше не позволяет разблокировать устройства лёгким образом, Samsung вызывает постоянные сложности дополнительными уровнями защиты. Moto G5P часто приводил к падению процесса сбора данных профайлера (simpleperf).
Требования
Для полноценного профилирования приложения вам потребуется телефон с Android 8 или новее (API 27). По опыту, профилирование с Android-ами более старых версий несёт больше приключений, чем пользы. По этой причине я рекомендую обзавестись полной линейкой Nexus устройств, так как они имеют старое железо и последние обновления Android-а.
Настройка Unity проекта
Для получения результата, вам придётся настроить Unity проект определённым образом.
Настройки в Build Settings
![image](https://habrastorage.org/webt/av/tr/ju/avtrjuzmsq9xk8egl88e_osxbz4.jpeg)
- В первую очередь, вы должны переключить Build System в “Gradle” и поставить галочку напротив “Export Project”. Таким образом мы получим готовый Android Studio проект, который мы и будем в дальнейшем профилировать. Профилирование готового APK тоже возможна, но более ограничена и требует больше подготовки
- Режим “Development Build” желательно выключить, так как таким образом получаемые тайминги в профайлере будут максимально близки к реальным
Настройки в Player Settings
![image](https://habrastorage.org/webt/-7/hz/ho/-7hzhozfm55alhksgs0jozpm488.jpeg)
- Установите Scripting Backend в IL2CPP. Если оставить Mono, то при нативном профилировании мы увидим много функций Mono, без информации как они соотносятся с C# кодом.
- Опция ‘C++ Compiler Configuration’ для Android ни на что не влияет (в Unity 2018.3). Можете поставить её в ‘Release’, возможно в будущих версиях Unity Android toolchain эта настройка будет влиять на настройки компилятора.
- Желательно ограничить ‘Target Architecture’ до ARMv7. Так вы сэкономите на времени компиляции и по опыту множество архитектур иногда вводит Android Studio в ступор.
- Также стоит установить ряд дополнительных настроек:
- Install Location — ‘Prefer external’
- Internet Access — ‘Require’
- Write Permission — ‘External (SDCard)’
Android Studio и другие профайлеры используют simpleperf для сбора статистики, и ему потребуется доступ к карте памяти для записи временных файлов.
Подготовка Gradle проекта
После того, как вы установили все настройки, соберите Unity проект. Вы должны получить папку с Gradle проектом.
![image](https://habrastorage.org/webt/vt/qq/fi/vtqqfixujd8ntg_ipoycbp9exwc.jpeg)
Unity по умолчанию собирает проект из расчёта, что вы планируете собирать из него финальный APK. Потому вся отладочная информацию из него удалена, но к счастью её можно вернуть. Для этого вам нужно подменить libil2cpp.so и libunity.so на версии с отладочной информацией.
libil2cpp.so
Это файл, который содержит весь C++ код, который IL2CPP сгенерировал из вашего C# кода. Unity генерирует всю необходимую для профилирования информацию, но для оптимизации размера APK, в процессе сборки вырезает её. Для того, чтобы вернуть её:
- Пойдите в папку вашего проекта
- Найдите в нём в подпапку Temp и перейдите в подпапку Temp/StagingArea/symbols/armeabi-v7a
- Найдите в ней ‘libil2cpp.so.debug’. Это версия ‘libil2cpp.so’ с отладочной информацией
- Теперь идите в Gradle проект и найдите в нем папку ‘\src\main\jniLibs\armeabi-v7a’
- Замените ‘libil2cpp.so’ файлом ‘libil2cpp.so.debug’
libunity.so
Это файл, который содержит low-level часть Unity Player. Так как мы делаем Release сборку, то Unity положила вам в проект файл без отладочной информации. Вам нужно подменить libunity.so на файл с символами.
- Пойдите в папку, где у вас установлена Unity
- Перейдите в папку "\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Development\Libs\armeabi-v7a\"
- Возьмите от туда файл libunity.so, и замените файл в вашем проекте, который лежит в папке ‘\src\main\jniLibs\armeabi-v7a’
Профилирование
Теперь вы можете начать профилировать в Android Studio, достаточно нажать кнопку запуска профайлера.
![image](https://habrastorage.org/webt/le/b3/yi/leb3yimardolvn0inirdf0hh-ak.jpeg)
Android Studio запустит приложение и начнётся сессия профилирования
![image](https://habrastorage.org/webt/q7/jv/na/q7jvnadenwr-9ixtxu-ac7l0exe.jpeg)
По умолчанию, Android Studio показывается графики, но не производит сэмплирование данных. Для запуска процесса, вам нужно кликнуть на треке CPU, чтобы профайлер переключится на вид CPU. При этом сверху окна появится дропдаун и кнопка ‘Record’.
![image](https://habrastorage.org/webt/_q/1t/qi/_q1tqikjnm-9vqltl3zvwh8t1am.jpeg)
Выберите Sampled ‘Native’ (В Android Studio 3.3 — C/C++ Native), и нажмите кнопку ‘Record’.
Так как запись производится на диск устройства, по опыту лучше не записывать более 5-8 секунд, многие устройства будут фейлится и на меньшем кол-ве данных (список проверенных устройств смотрите в конце статьи).
Для получения результата нажмите ‘Stop’ и потом красный квадрат, чтобы прервать сессию. Сложно понять задумку авторов, но если вы не остановите запись полностью, то профайлер не всегда начинает анализ полученных данных и ваш отрезок с данным уедет в далёкие дали.
![image](https://habrastorage.org/webt/nc/s6/wb/ncs6wbzxzmnl2ekezsbeikdalto.jpeg)
После этого остаётся только немного подождать, через 30-50 секунд профайлер выдаст вам результат. Если всё настроено верно, вы получите capture со всем именами функций
![image](https://habrastorage.org/webt/ft/ws/9a/ftws9av2ird_wbbvppc3af9smey.jpeg)
Известные особенности
- Наиболее стабильные результаты можно получить на root-ованных девайсах
- Не используйте Samsung-и, на них есть множество защитных наворотов, которые мешают отладке
- Во множестве мест ваш колстэк будет уходить в функции вида ‘kernel.kptr + адрес’. Это вызовы внутри Android Kernel, которые защищены из-за политики безопасности. На рутованном устройстве защиту можно отключить:
- Запустите `adb shell`
- Запустите `su` чтобы получить права root-пользователя
- Выполните ‘sysctl -w kernel.kptr_restrict=0’ — это снимет защите с kernel-а
- [!] После окончания отладки, выполните ‘sysctl -w kernel.kptr_restrict=1’. Некоторые устройства иначе при перезагрузке не смогут загрузить OS. Во многих случаях лечится только перепрошивкой чистого ядра.
- Если Android Studio часто падает, можно попробовать увеличить heap Java VM:
- 2Gb — для проектов среднего размера (‘-Xmx2g’)
- 4Gb — для больших проектов (‘-Xmx4g’)
- На нерутованных устройствах иногда улучшает ситуацию переключение ядра в ‘permissive mode’
- Выполните команду ‘adb shell setenforce 0’
Unity специфика
- Главный поток Unity называется UnityMain, но вы можете увидеть множество UnityMain при профилировании. Это пользовательские потоки, которые вы создаете внутри C# кода. По умолчанию они получают такое же имя. Главный поток Unity обычно легко отличить, так как он будет самый нагруженный.
- Графический поток называется UnityGfxWorkerW
- Потоки Unity Job системы называются Worker Thread
- К сожалению некоторые wait-функции, которые применяет Unity (futex-ы), Android Studio показывает и считает не как wait-time, а как активность.
- Когда вы будете смотреть call graph в Top Down view, вам придётся пройти через множество уровней Java-вызовом, к сожалению отфильтровать в Android Studio это никак нельзя.
![image](https://habrastorage.org/webt/f_/it/lp/f_itlpp-dlvolgu5pb-ze7rz4oe.jpeg)
Рекомендованные устройства
Мы для экспериментов использовали следующие устройства:
- Samsung Galaxy S8
- Google Pixel 2XL
- Google Pixel
- Sony Xperia XA1
- Huawei Honor 7
- Huawei Nexus 6P
- Moto G5P
- Asus Nexus 7 (2013)
Все устройства были зарутованы, для Huawei Nexus 6P мы собрали ядро и AOSP. По результату самыми беспроблемными и простыми для работы оказались Google и Sony. У Sony есть отличный сайт для разработчиков — Open Devices. Huawei больше не позволяет разблокировать устройства лёгким образом, Samsung вызывает постоянные сложности дополнительными уровнями защиты. Moto G5P часто приводил к падению процесса сбора данных профайлера (simpleperf).