Анализ производительности в реальном времени

Рейтинг: 64% · 16 голосов
Полный курс по Android Debug Bridge: установка, подключение, shell, логи, dumpsys, автоматизация, root, беспроводная отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
android_roman
Сообщения: 45
Зарегистрирован: 11 май 2026, 05:31

Анализ производительности в реальном времени

Сообщение android_roman »

АкадемияADB: Android Debug BridgeГлава 10 из 29
Оглавление курса (29)
  1. Введение в Android Debug Bridge
  2. Установка и настройка рабочей среды
  3. Подключение устройства (проводное и беспроводное)
  4. Базовые команды ADB и управление сервером
  5. Команды состояния и перезагрузки
  6. Навигация по файловой системе
  7. Управление пакетами приложений
  8. Логирование с помощью logcat
  9. Системные дампы и диагностика (dumpsys)
  10. Анализ производительности в реальном времени (вы здесь)
  11. Эмуляция ввода (input)
  12. Управление Activity и Intent (am)
  13. Работа с оконным менеджером (wm)
  14. Захват экрана и запись видео
  15. Root-доступ и его применение
  16. Модификация системных настроек через settings
  17. Команды для поставщиков контента (content)
  18. Резервное копирование и восстановление (backup)
  19. Проброс портов и туннелирование
  20. Беспроводная отладка (Wi-Fi)
  21. Взаимодействие с эмуляторами
  22. Написание скриптов на Bash/CMD/PowerShell
  23. ADB в языках программирования
  24. Автоматизация тестирования с ADB
  25. Безопасность и лучшие практики
  26. ADB на Android TV, Wear OS и IoT
  27. Восстановление и низкоуровневые операции
  28. Расширенные возможности оболочки и инструменты
  29. Отладка самого ADB и устранение неполадок
В главе 9 мы снимали статичные дампы через dumpsys. Это хорошо для разбора полетов постфактум, но когда приложение тормозит прямо сейчас, нужны живые цифры: кто грузит процессор, куда уходит память, не ушел ли телефон в троттлинг. Весь инструментарий уже лежит на устройстве, ставить ничего не надо. Разберем его по слоям: от общей картины системы до профилирования отдельных методов.

top: процессы и потоки:

На Android 8 и новее команда top реализована в toybox, флаги отличаются от привычного GNU top, и это первый источник путаницы. Запуск без параметров дает интерактивный режим, выход по q или Ctrl+C:

Код: Выделить всё

adb shell top
Типичная шапка на 8-ядерном устройстве с Android 15:

Код: Выделить всё

Tasks: 689 total,   2 running, 687 sleeping,   0 stopped,   0 zombie
  Mem:      7.2G total,      6.9G used,      311M free,       14M buffers
 Swap:      4.0G total,      1.1G used,      2.9G free,      2.7G cached
800%cpu  52%user   0%nice  41%sys 692%idle   2%iow   9%irq   4%sirq   0%host
  PID USER         PR  NI VIRT  RES  SHR S[%CPU] %MEM     TIME+ ARGS
 2841 u0_a312      10 -10  28G 412M 188M S  64.3   5.5  12:41.30 com.example.game
 1287 system       18  -2  32G 680M 290M S  21.0   9.1 188:12.55 system_server
Строка 800%cpu означает 8 онлайн-ядер, по 100% на каждое. Соответственно проценты по процессу считаются относительно одного ядра, поэтому 200% это два полностью занятых ядра, и пугаться значений выше 100 не нужно.

Для скриптов обязателен пакетный режим -b, иначе toybox начнет слать escape-последовательности перерисовки и вывод превратится в кашу:

Код: Выделить всё

adb shell top -b -n 1 -m 15
Здесь -n 1 означает одну итерацию и выход, -m 15 ограничивает список пятнадцатью строками. Интервал обновления задается через -d (можно дробный, -d 0.5), фильтр по пользователю через -u.

Потоки конкретного процесса показывает флаг -H:

Код: Выделить всё

adb shell top -H -b -n 1 -p 2841
В колонке ARGS вместо пакета появятся имена потоков: главный поток называется как процесс, рядом RenderThread, hwuiTask0, FinalizerDaemon, пулы binder:2841_1 и так далее. Если у игры 90% времени съедает не главный поток, а какой-нибудь Worker-7, вы только что сэкономили день на угадывании.

vmstat: системная картина одной строкой:

Когда нужно понять, что происходит с системой в целом, а не с отдельным процессом, удобен vmstat. Формат: задержка в секундах и число итераций, флаг -n печатает заголовок один раз:

Код: Выделить всё

adb shell vmstat -n 1 5

Код: Выделить всё

procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 2  0 1153024 295112  14336 2611200    0    0    12    40 4123 9870 14  9 76  1
 3  0 1153024 287400  14336 2612160    0  256     0   124 6011 14250 31 12 56  1
На что смотреть: r это очередь готовых к выполнению задач, если она стабильно больше числа ядер, процессору не хватает мощности. si/so показывают обмен со swap (на Android это обычно zram), постоянные ненулевые значения означают дефицит памяти. cs это переключения контекста, аномально высокие числа намекают на войну потоков за блокировки. wa это доля ожидания ввода-вывода.

dumpsys cpuinfo: кто ел процессор:

Код: Выделить всё

adb shell dumpsys cpuinfo

Код: Выделить всё

Load: 9.12 / 8.4 / 7.95
CPU usage from 412433ms to 187229ms ago (2026-06-11 14:02:11.482 to 2026-06-11 14:05:56.687):
  47% 2841/com.example.game: 31% user + 15% kernel / faults: 18223 minor 12 major
  18% 1287/system_server: 12% user + 5.8% kernel / faults: 9911 minor
  6.1% 988/surfaceflinger: 3.7% user + 2.4% kernel
71% TOTAL: 44% user + 22% kernel + 1.2% iowait + 2.4% irq + 0.9% softirq
Частая ошибка: считать этот вывод мгновенным срезом. На самом деле system_server периодически снимает статистику, и dumpsys cpuinfo показывает дельту между двумя последними снимками, интервал написан в заголовке. Если нужен срез за конкретные 10 секунд нагрузки, вызовите команду до и после теста и сравнивайте сами, либо берите top. Load это стандартный loadavg за 1/5/15 минут. Проценты по процессам здесь тоже относительно одного ядра, а строка TOTAL нормирована на все ядра, поэтому больше 100% не бывает. Major faults (подгрузка страниц с накопителя) в больших количествах означают, что системе тесно в памяти.

dumpsys meminfo: память процесса:

Код: Выделить всё

adb shell dumpsys meminfo com.example.game
Самая полезная часть вывода, App Summary:

Код: Выделить всё

 App Summary
                       Pss(KB)                        Rss(KB)
                        ------                         ------
           Java Heap:    26544                          33712
         Native Heap:   182340                         188412
                Code:    48122                         110233
               Stack:     1980                           1992
            Graphics:   145200                         145200
       Private Other:    12244
              System:    18733

           TOTAL PSS:   435163   TOTAL RSS:   501480   TOTAL SWAP PSS:     6233

 Objects
               Views:      512         ViewRootImpl:        1
         AppContexts:        8           Activities:        2
PSS это пропорционально разделенная память (общие страницы делятся между процессами), именно по ней система ранжирует кандидатов на убийство. RSS это физически занятые страницы целиком. Graphics это буферы GPU, у игр и приложений с тяжелым UI они легко перевешивают обе кучи. Секция Objects бесценна для ловли утечек: вышли со всех экранов, а Activities: 7, значит кто-то держит ссылки.

Полезные вариации:

Код: Выделить всё

adb shell dumpsys meminfo -s com.example.game
adb shell dumpsys meminfo 2841
adb shell dumpsys meminfo
Флаг -s печатает только сводку, по pid удобно смотреть конкретный процесс многопроцессного приложения (иначе для пакета с процессами вида :remote вывалится несколько блоков). Без аргументов получите рейтинг всех процессов по PSS плюс итоги Total RAM, Free RAM и Lost RAM. И не сравнивайте абсолютные PSS между разными устройствами и версиями Android, методика подсчета меняется, сравнивайте только динамику на одном аппарате.

am profile: профилировщик методов:

Когда известно, какой процесс виноват, но непонятно, какой код, подключаем профилировщик ART через Activity Manager. Процесс обязан быть отлаживаемым (android:debuggable="true" в манифесте либо userdebug-сборка системы), на релизном приложении получите ошибку SecurityException: Process not debuggable.

Код: Выделить всё

adb shell am profile start --sampling 1000 com.example.game /data/local/tmp/game.trace
adb shell am profile stop com.example.game
adb pull /data/local/tmp/game.trace
Флаг --sampling 1000 включает выборочное профилирование с шагом 1000 микросекунд, накладные расходы минимальны. Без него включается инструментальный режим с записью каждого входа и выхода из метода: данные точнее, но приложение замедляется в разы, тайминги искажаются, для поиска тормозов он почти бесполезен. На Android 14+ есть еще --clock-type для выбора между wall и thread-cpu временем. Файл пишет сам процесс приложения, поэтому путь должен быть ему доступен на запись, /data/local/tmp подходит, а вот запись в /sdcard без разрешений упадет с Permission denied.

Профилирование холодного старта:

Код: Выделить всё

adb shell am start -n com.example.game/.MainActivity --start-profiler /data/local/tmp/start.trace --sampling 500
adb shell am profile stop com.example.game
Полученный .trace открывается перетаскиванием в Android Studio Profiler или в ui.perfetto.dev.

Системные трассировки: perfetto и atrace:

Профилировщик показывает только Java/ART-уровень одного процесса. Полную картину (планировщик, частоты, отрисовка кадров, binder) дает perfetto, штатный с Android 9 и включенный по умолчанию с Android 11:

Код: Выделить всё

adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace -t 15s sched freq idle gfx view wm am binder_driver
adb pull /data/misc/perfetto-traces/trace.perfetto-trace
На user-сборках perfetto имеет право писать только в /data/misc/perfetto-traces, попытка указать другой путь закончится Permission denied. На Android 9-10 сначала включите демона: adb shell setprop persist.traced.enable 1. Файл анализируется в ui.perfetto.dev, там видно каждый кадр, каждое пробуждение потока и текущую частоту каждого ядра на таймлайне. Старый systrace.py из Platform Tools давно выпилен, его роль целиком у perfetto. Низкоуровневый atrace остался, список доступных категорий смотрят так:

Код: Выделить всё

adb shell atrace --list_categories
am trace-ipc: охота на лишние Binder-вызовы:

Каждый вызов системного API (PackageManager, WindowManager, settings) это IPC через Binder, и вызовы в цикле или на каждом кадре заметно бьют по производительности. Встроенный трассировщик:

Код: Выделить всё

adb shell am trace-ipc start
adb shell am trace-ipc stop --dump-file /data/local/tmp/ipc.txt
adb pull /data/local/tmp/ipc.txt
Между start и stop повоспроизводите проблему руками. В дампе по каждому процессу будут счетчики исходящих binder-транзакций и Java-стеки мест вызова. Классическая находка: сотни обращений к getPackageInfo из onDraw. Файл для --dump-file опять же кладите в /data/local/tmp.

Температура и частоты процессора:

Тормоза под нагрузкой часто объясняются не кодом, а троттлингом. Состояние термальной подсистемы (Android 10+):

Код: Выделить всё

adb shell dumpsys thermalservice

Код: Выделить всё

Thermal Status: 2
Current temperatures from HAL:
	Temperature{mValue=42.7, mType=3, mName=SKIN, mStatus=2}
	Temperature{mValue=67.1, mType=0, mName=CPU, mStatus=0}
	Temperature{mValue=38.9, mType=2, mName=BATTERY, mStatus=0}
Thermal Status идет по шкале от 0 (NONE) до 6 (SHUTDOWN), уже при 3 (SEVERE) система агрессивно режет частоты. Тип 3 это температура корпуса (skin), по ней обычно и троттлят. Если вендор не реализовал thermal HAL как следует, список может быть пуст, тогда читаем сырые зоны ядра:

Код: Выделить всё

adb shell 'for z in /sys/class/thermal/thermal_zone*; do echo $z $(cat $z/type) $(cat $z/temp); done'
Значения чаще всего в миллиградусах (42700 = 42.7 C), но встречаются вендоры с десятыми долями, ориентируйтесь на порядок величины. Температуру батареи быстрее взять из dumpsys battery, поле temperature: 312 означает 31.2 C.

Текущие частоты ядер:

Код: Выделить всё

adb shell 'for c in /sys/devices/system/cpu/cpu[0-9]*; do echo $c $(cat $c/cpufreq/scaling_cur_freq 2>/dev/null); done'

Код: Выделить всё

/sys/devices/system/cpu/cpu0 1804800
/sys/devices/system/cpu/cpu4 2496000
/sys/devices/system/cpu/cpu7 2841600
Частоты в килогерцах, 2841600 это 2.84 ГГц. Потолок и регулятор смотрят рядом: cpuinfo_max_freq и scaling_governor в том же каталоге. Файл scaling_cur_freq читается обычным shell, а вот cpuinfo_cur_freq на многих ядрах доступен только root. Выключенные ядра файлов не отдают, отсюда 2>/dev/null. Простейший монитор троттлинга на время прогона теста:

Код: Выделить всё

adb shell 'while true; do echo "$(date +%T) skin=$(cat /sys/class/thermal/thermal_zone0/temp) cpu7=$(cat /sys/devices/system/cpu/cpu7/cpufreq/scaling_cur_freq)"; sleep 2; done'
Остановка по Ctrl+C. И тестируйте троттлинг с отключенным кабелем питания по Wi-Fi отладке из главы 3: зарядка сама греет аппарат и портит замеры.

Сводка частых ошибок:

top без -b в скриптах дает мусор из escape-кодов. dumpsys cpuinfo это не реальное время, а дельта за интервал из заголовка. am profile падает на неотлаживаемых сборках и при записи в недоступный путь. perfetto на user-сборке пишет только в /data/misc/perfetto-traces. Температурные зоны врут в единицах измерения, проверяйте порядок величин. PSS между устройствами не сравнивается.

Этого набора хватает, чтобы локализовать почти любую просадку: top и vmstat показывают где, meminfo и cpuinfo показывают кто, am profile и perfetto показывают почему. В главе 22 мы обернем эти команды в скрипты для регулярных замеров, а в главе 24 встроим их в автотесты.
👍6 ❤️4 🔥1 😄 🤔1
✔ Лучший ответ сформирован автоматически — zfs2
android_roman писал(а):проценты по процессу считаются относительно одного ядра, поэтому 200% это два полностью занятых ядра то есть на 8-ядернике теоретический потолок 800%? у меня на Snapdragon 8 Gen 2 игра в top стабильно держит 130-150%, при этом fps норм. это уже повод лезть в perfetto или пока живем?
Перейти к ответу →
Аватара пользователя
zfs2
Сообщения: 1
Зарегистрирован: 11 май 2026, 09:44

Re: Анализ производительности в реальном времени

Сообщение zfs2 »

✔ Лучший ответ — сформирован автоматически
android_roman писал(а):проценты по процессу считаются относительно одного ядра, поэтому 200% это два полностью занятых ядра
то есть на 8-ядернике теоретический потолок 800%? у меня на Snapdragon 8 Gen 2 игра в top стабильно держит 130-150%, при этом fps норм. это уже повод лезть в perfetto или пока живем?
👍1 ❤️1 🔥 😄 🤔1
Аватара пользователя
gutterbo
Сообщения: 1
Зарегистрирован: 14 май 2026, 10:37

Re: Анализ производительности в реальном времени

Сообщение gutterbo »

попробовал am profile на релизной сборке и поймал тот самый Process not debuggable. нашел обход: с Android 10 можно добавить в манифест <profileable android:shell="true"/>, тогда sampling через am profile и simpleperf работает и без debuggable. инструментальный режим все равно не дает, но для замеров на проде он и не нужен.
👍 ❤️ 🔥1 😄 🤔
Аватара пользователя
bilongo
Сообщения: 2
Зарегистрирован: 12 май 2026, 16:03

Re: Анализ производительности в реальном времени

Сообщение bilongo »

dumpsys thermalservice на моем Redmi выдает пустой Current temperatures from HAL, хотя зоны в /sys читаются нормально, их там штук 60. так что совет про сырые зоны очень в тему. и да, у меня в части зон temp лежит как раз в десятых долях, 427 вместо 42700, без проверки порядка величины легко облажаться.
👍 ❤️1 🔥2 😄 🤔
Аватара пользователя
mmontyj
Сообщения: 1
Зарегистрирован: 21 май 2026, 22:54

Re: Анализ производительности в реальном времени

Сообщение mmontyj »

а если устройство на Android 9 и после setprop perfetto все равно ругается? у меня помогла перезагрузка после установки проперти, демон traced поднялся только после нее. может кому сэкономит полчаса гугления.
👍 ❤️2 🔥 😄 🤔
Ответить
← Предыдущая глава
Системные дампы и диагностика (dumpsys)
Следующая глава →
Эмуляция ввода (input)

Все главы курса «ADB: Android Debug Bridge»

Поделиться темой: ✈ Telegram VK

Вернуться в «ADB: Android Debug Bridge»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость