С развитием и ростом популярности ОС Android количество и разнообразие устройств под её управлением неуклонно растёт. Из-за различий в архитектуре, предназначении и оптимизации скорость и стабильность работы исполняемого кода может значительно изменяться. Поэтому, для обеспечения стабильности и оптимизации работы приложений и ОС, особенно использующих особенности конкретной архитектуры, платформы, или кода, портированного с других платформ, стоит особо внимание уделить процессу отладки кода под Андроид. В этой статье пойдёт речь о ключевых моментах и особенностях работы с native-кодом под Android. Всем, кому интересен этот мануал, прошу под кат.
![Отладка native кода под Android: ручное и автоматизированное тестирование image](https://www.pvsm.ru/images/otladka-native-koda-pod-Android-ruchnoe-i-avtomatizirovannoe-testirovanie.png)
В далёком 2007 году корпорация Google выпустила операционную систему нового поколения – Android.
Её появление совершило настоящую революцию на рынке мобильных устройств, подавив гегемонию Microsoft Widows Mobile, Apple iOS и Symbian OS, в последствие став самой популярной и массовой операционной системой для мобильных систем в мире. До сих пор рост популярности системы продолжается. За прошедшие шесть лет Android очень сильно разросся и развился, и спектр поддерживаемых устройств продолжает увеличиваться. Теперь это операционная система используется не только в мобильных телефонах, но также и в планшетах, телевизорах, плеерах, фотоаппаратах, ноутбуках, неттопах и прочих экзотических вещах, вплоть до военной техники. Начинки этих устройств могут весьма заметно отличаться друг от друга: за прошедшие годы перечень поддерживаемых архитектур расширился с ARM до MIPS и х86, появилась поддержка различной периферии, не говоря о непременно развивающемся Android API.
Такое огромное количество различных устройств, несмотря на их функциональные и архитектурные различия, управляются одной и той же системой, за вычетом платформозависимых библиотек и оптимизаций под конкретные устройства. Вполне закономерно, что сборки Android и даже зачастую библиотеки приложений отличаются друг от друга, зачастую даже в зависимости от вендора, так как требования к производительности лишь увеличиваются в условиях жесткой конкуренции растущего рынка устройств на платформе Android.
![Отладка native кода под Android: ручное и автоматизированное тестирование Отладка native кода под Android: ручное и автоматизированное тестирование](https://www.pvsm.ru/images/otladka-native-koda-pod-Android-ruchnoe-i-avtomatizirovannoe-testirovanie-3.png)
Тем не менее, обеспечение унификации и кроссплатформености, быстроты работы приложений – одно из приоритетных направлений сегодняшнего рынка высокопроизводительных приложений и сборок системы. Это, в первую очередь, означает повышенные требования к самой операционной системе, способной работать и делать это эффективно на различном аппаратном обеспечении, так и высокие требования к приложениям для Android.
![Отладка native кода под Android: ручное и автоматизированное тестирование Отладка native кода под Android: ручное и автоматизированное тестирование](https://www.pvsm.ru/images/otladka-native-koda-pod-Android-ruchnoe-i-avtomatizirovannoe-testirovanie-4.jpg)
Одним из самых эффективных решений для оптимизации и ускорения работы приложений, системы и её компонентов является оптимизация на родном, native-уровне. Другими словами, использование для приложений и системы оптимизированных, часто аппаратно-зависимых библиотек и кода, а в глобальном смысле – целого ряда библиотек, поставляющихся для каждой из целевых платформ. Такое многообразие аппаратных платформ, различных версий операционной системы, платформ API и требований к обеспечению стабильной и быстрой работы приложений требует значительных усилий на тестирование приложений, библиотек под конкретные конфигурации.
Тем не менее, одним из самых удобных инструментов для разработчика Android до сих пор является пакет Android NDK – Android Native Development Kit, включающий в себя почти всё необходимое для разработки и отладки приложений, библиотек и самой системы.
В данной статье я рассмотрю его использование для тестирования и отладки native-кода Android и автоматизацию тестирования на примере такого фреймворка, как DejaGnu.
Android развивается очень динамично, и теперь в поставку DejaGnu входит почти что всё необходимое для быстрого старта тестирования – как с Android NDK, так и без него. Главной задачей данной статьи будет рассказ о нюансах и особенностях тестирования под Android и некоторых подводных камнях, с которыми могут столкнуться инженеры.
Для начала небольшая ремарка: Android, как известно, является Unix-like операционной системой, очень схожей с обычным Linux или BSD, и большинство исходного кода Android следует лицензии, ASL2.0. Кто работал с Android, знает, что немалое количество модулей Android перешли из BSD в большей степени, чем из Linux. Тем не менее, всё, что касается native-кода и архитектуры, если не вдаваться в подробности, очень близко и известно всем тем, кто использовал Linux. Поэтому для разработки и отладки кода вполне возможно использовать точно такие же или схожие инструменты, что и для Unix-like систем. В случае компиляторов – это gcc, clang (llvm), icc и прочие. То же самое можно сказать и насчёт всего GCC toolchain и некоторых других утилит, которые либо уже портированы, либо портировать не так сложно.
Главной причиной включения в приложение native-кода или использования native-библиотек, native-исполнимых файлов является стремление увеличить производительность или переиспользовать ранее написанный код C/C++/ASM с Linux/Unix-like систем.
Преимущества использования native-кода:
• Высокая производительность
• Прямое использование CPU/HW особенностей
• Возможность переиспользования существующего Linux кода
Недостатки использования native-кода:
• Индивидуальная настройка под CPU/HW
• Недостаточная поддержка системных библиотек
Плюсы и минусы требуют тщательного тестирования, как по качеству кода, стабильности, так и по его производительности на разных платформах. Несмотря на то, что одним из постулатов Android является независимость от железа, для обеспечения работы на различных конфигурациях, в действительно приложения с native-кодом поставляются в так называемых fat binary (apk, включающие в себя native-код/библиотеки для всех возможных конфигураций оборудования, под которым будет работать приложение). Несмотря на то, что в идеале NDK компилятор должен создавать функционально равнозначный код для различных конфигураций и, следовательно, библиотеки для fat binary (или системные – для образа) должны быть равнозначными — это, к сожалению, не всегда соответствует действительности и требует проверки, особенно когда дело касается оптимизации (Neon и SSE например) и использования сторонних библиотек.
![Отладка native кода под Android: ручное и автоматизированное тестирование Отладка native кода под Android: ручное и автоматизированное тестирование](https://www.pvsm.ru/images/otladka-native-koda-pod-Android-ruchnoe-i-avtomatizirovannoe-testirovanie-6.png)
Кроме того, с развитием ОС Android и устройств под её управлением, закономерно меняются различные версии ОС, поставка этой ОС, а так же специфика устройств. В некоторых случаях для разработчика оправдано использование каких-то оптимизаций или иного компилятора для сборки native-кода. Оценка производительности, правильности и стабильности этого кода на различных конфигурациях – задача тестировщика, занимающегося Android Native тестированием.
Процесс сборки, отладки и тестирования native приложений под Android не сильно отличается от того же процесса на Linux, с единственной разницей, что мы собираем бинарные файлы (исполнимые и библиотеки) не в Android, а на хосте (Linux, MacOS, Windows) и исполняем на устройстве Android (физическом или эмуляторе). Поэтому универсальным средством коммуникации хоста с устройством под управлением Android является adb – Android Debug Bridge, входящий в состав Android SDK. Для сборки и отладки приложений целесообразно использовать тот toolchain и API, который нам нужен, а так же, при необходимости (для c++), интересующую нас версию библиотеки stdc++.
Native-сборка Android может различаться по:
- версиям используемого API:
- версиям используемого libstdc++:
- архитектуре устройства (target):
- разрядности (32, 64 или x32) target:
- билд-хосту и его разрядности:
Впрочем, с точки зрения разработчика и тестировщика, различий между получаемым кодом в зависимости от билд-хоста быть не должно, и использовать для проверки и тестирования различные конфигурации билд-хостов вовсе не обязательно.
Количество вариантов для тестирования угрожающе разрастается, и это не беря в расчёт оптимизации по CPU инструкциям: neon, core-avx2, core-i7, atom, slm; по размерускорости и прочие. Все это декартово произведение вариантов полученного кода (исходного и бинарного) является отправной точкой для тестирования. В случае же, когда целью тестирования является само устройство или какой-то кастомная сборка Android, то, вполне вероятно, главным весомым различием будет библиотека bionic – аналог библиотеки libc для Android.
Закончив вводную теорию, самое время перейти к практике.
Инструменты для ручной сборки и тестирования
Все необходимые инструменты входят в поставку Android NDK, которая есть для 32 и 64 бит под Linux, MacOS, Windows.
Создание и запуск приложения
gcc
bash-4.2$ #начнём с обычного hello_world.c
bash-4.2$ cat ./hello_world.c
#include <stdio.h>
int main(void)
{
printf(“Hello, World!n”);
return 0;
}
bash-4.2$ #Соберём бинарник для Android, используя gcc 4.7 под x86, платформа 18
bash-4.2$ /users/NDK_current/toolchains/x86-4.7/prebuilt/linux-x86/bin/i686-linux-android-gcc –sysroot=/users/NDK_current/platforms/android-18/arch-x86 ./hello_world.c -o ./hello_world.exe
bash-4.2$ echo $?
0
bash-4.2$ #собралось, хорошо
bash-4.2$ #теперь запустим
bash-4.2$ ./hello_world.exe
bash-4.2$ #плохо, нельзя запустить на хосте
bash-4.2$ #давайте посмотрим на доступные устройства
bash-4.2$ adb devices
List of devices attached
0146AFFC18020012 device
bash-4.2$ #хорошо, давайте выберем какое-нибудь и проверим отклик
bash-4.2$ adb -s 0146AFFC18020012 shell echo ‘Hello, Android!’
Hello, Android!
bash-4.2$ #работает, но каждый раз вбивать серийник неудобно
bash-4.2$ export ANDROID_SERIAL=0146AFFC18020012
bash-4.2$ #поэтому заведём переменную
bash-4.2$ adb shell echo ‘Hello, Android!’
Hello, Android!
bash-4.2$ #так-то лучше, теперь отправим бинарник на устройство
bash-4.2$ adb push ./hello_world.exe /data/local/
bash-4.2$ #что-то отправилось, теперь запустим
bash-4.2$ adb shell /data/local/hello_world.exe
Hello, World!
bash-4.2$ #adb ответил, что всё хорошо, теперь убедимся ещё раз, что точно запустилось
bash-4.2$ adb shell “/data/local/hello_world.exe && echo $?”
Hello, World!
0
bash-4.2$ #работает, но не всегда под рукой есть устройство или эмулятор
bash-4.2$ #можно сделать статическую сборку, которая будет работать везде на совместимых архитектурах
bash-4.2$ #файл будет больше, но мы сможем запустить как на хосте (linux, mac, windows)
bash-4.2$ #так и на устройстве, где может каких-то зависимостей и не быть
bash-4.2$ #чтобы это сделать, надо добавить флажок -static
/users//NDK_current/toolchains/x86-4.7/prebuilt/linux-x86/bin/i686-linux-android-gcc –sysroot=/users/ /NDK_current/platforms/android-18/arch-x86 -static ./hello_world.c -o ./hello_world.exe
bash-4.2$ echo $?
0
bash-4.2$ #собралось, но не всегда это возможно из-за громоздкости связанных библиотек и наличия нужных статичных версий
bash-4.2$ ./hello_world.exe
Hello, World!
bash-4.2$ echo $?
0
bash-4.2$ #работает, теперь мы можем запустить и протестировать приложение и на хосте
bash-4.2$
g++
bash-4.2$ #теперь давайте сделаем тоже самое для g++
bash-4.2$ cat ./hello_world.C
#include <iostream>
int main(void)
{
std::cout << “Hello, World!n”;
return 0;
}
bash-4.2$ #если мы будем использовать STL, то
bash-4.2$ #нам дополнительно понадобится указать пути к библиотекам и заголовочным файлам
bash-4.2$ #а так же сообщить компоновщику, какую библиотеку надо добавить
bash-4.2$ #обратите внимание, что инструкция -l%STL_LIB% должна быть в конце командной строки!
bash-4.2$ #динамический вариант имеет суффикс _shared, а статический _static соответственно
bash-4.2$ #теперь добавим к вызову g++ -L, -I и одну из библиотек – gnustl/stlport/gabi
bash-4.2$ /users/ /NDK_current/toolchains/x86-4.7/prebuilt/linux-x86/bin/i686-linux-android-g++ –sysroot=/users/NDK_current/platforms/android-18/arch-x86 -I/users/NDK_current/sources/cxx-stl/gnu-libstdc++/4.7/include -I/users/NDK_current/sources/cxx-stl/gnu-libstdc++/4.7/libs/x86/include -L/users//NDK_current/sources/cxx-stl/gnu-libstdc++/4.7/libs/x86 ./hello_world.C -o ./hello_world.exe -lgnustl_shared
bash-4.2$ echo $?
0
bash-4.2$ #собралось, но не всегда это возможно из-за громоздкости связанных библиотек и/или наличия нужных статичных/динамических версий
bash-4.2$ adb push ./hello_world.exe /data/local/
bash-4.2$ #что-то отправилось, теперь запустим
bash-4.2$ adb shell “/data/local/hello_world.exe && echo $?”
soinfo_link_image(linker.cpp:1635): could not load library “libgnustl_shared.so” needed by “/data/local/hello_world.exe”; caused by load_library(linker.cpp:745): library “libgnustl_shared.so” not foundCANNOT LINK EXECUTABLE
bash-4.2$ #не работает, не хватает библиотеки
bash-4.2$ #libgnustl_shared.so не входит в поставку Android, поэтому её надо скопировать на устройство
bash-4.2$ adb push /users//NDK_current/sources/cxx-stl/gnu-libstdc++/4.7/libs/x86/libgnustl_shared.so /data/local/libgnustl_shared.so
bash-4.2$ #переписали, запустим ещё раз, явным образом указав LD_LIBRARY_PATH
bash-4.2$ adb shell “export LD_LIBRARY_PATH=/data/local/:$LD_LIBRARY_PATH && /data/local/hello_world.exe && echo $?”
Hello, World!
0
bash-4.2$ #либо перенесём библиотеку в путь по умолчанию – /system/lib
bash-4.2$ #запустим
bash-4.2$ adb shell “/data/local/hello_world.exe && echo $?”
Hello, World!
0
bash-4.2$
В случае совпадения архитектуры host и target (обычно речь идёт о x86) и наличия root привилегий, вполне возможно использовать при желании хитрый трюк с запуском Android x86 бинарников на хосте. Для этого нужно явным образом использовать dynamic linker для Android на системе (/system/bin/linker), а так же использовать non-stripped версию bionic в путях (LD_LIBRARY_PATH). См пример Makefile: (https://android.googlesource.com/platform/bionic/+/master/tests/Android.mk: bionic-unit-tests-run-on-host).
# -----------------------------------------------------------------------------
# Run the unit tests built against x86 bionic on an x86 host.
# -----------------------------------------------------------------------------
ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),x86 x86_64))
ifeq ($(TARGET_ARCH),x86)
LINKER = linker
else
LINKER = linker64
endif
# gtest needs ANDROID_DATA/local/tmp for death test output.
# Make sure to create ANDROID_DATA/local/tmp if doesn't exist.
# bionic itself should always work relative to ANDROID_DATA or ANDROID_ROOT.
bionic-unit-tests-run-on-host: bionic-unit-tests $(TARGET_OUT_EXECUTABLES)/$(LINKER) $(TARGET_OUT_EXECUTABLES)/sh
if [ ! -d /system -o ! -d /system/bin ]; then
echo "Attempting to create /system/bin";
sudo mkdir -p -m 0777 /system/bin;
fi
mkdir -p $(TARGET_OUT_DATA)/local/tmp
cp $(TARGET_OUT_EXECUTABLES)/$(LINKER) /system/bin
cp $(TARGET_OUT_EXECUTABLES)/sh /system/bin
ANDROID_DATA=$(TARGET_OUT_DATA)
ANDROID_ROOT=$(TARGET_OUT)
LD_LIBRARY_PATH=$(TARGET_OUT_SHARED_LIBRARIES)
$(TARGET_OUT_DATA_NATIVE_TESTS)/bionic-unit-tests/bionic-unit-tests
endif
endif
Такой трюк, например, актуален для исполнения бинарных файлов без эмулятора (в случае отсутствия 64-битного образа или использования -mx32).
GCOV и профили
bash-4.2$ #некоторые файлы, вполне возможно, что-то порождают, и это мы так же можем вытащить с устройства
bash-4.2$ #скомпилируем, например, файл порождающий coverage информацию
bash-4.2$ #обратите внимание, что компилируете вы на хосте, и надо явным образом будет указать директорию профилирования
bash-4.2$ #это можно сделать во время компиляции, добавив флаг -fprofile-dir=%android_exec_dir%
bash-4.2$ #либо перед исполнением, на устройстве, выставив GCOV_PREFIX и GCOV_PREFIX_STRIP
bash-4.2$ #так же обратите внимание, что манипуляции с env действительны лишь на время сессии adb (открытой shell или команды adb shell %command%
bash-4.2$ /users//NDK_current/toolchains/x86-4.7/prebuilt/linux-x86/bin/i686-linux-android-gcc –sysroot=/users//NDK_current/platforms/android-18/arch-x86 ./gcov-1.c -fprofile-arcs -fprofile-dir=. -ftest-coverage -lm -o ./gcov-1.exe
bash-4.2$ adb push ./gcov-1.exe /data/local/gcov-1.exe
bash-4.2$ #adb shell “export GCOV_PREFIX=/data/local && export GCOV_PREFIX_STRIP=13 && /data/local/gcov-1.exe && echo $?”
bash-4.2$ unset GCOV_PREFIX && unset GCOV_PREFIX_STRIP && cd /data/local && ./gcov-1.exe && echo $?”
0
bash-4.2$ adb shell ls /data/local/gcov-1.gcda
/data/local/gcov-1.gcda
bash-4.2$ #вытащим профиль
bash-4.2$ adb pull /data/local/gcov-1.gcda .
bash-4.2$ ls ./gcov-1.gcda
./gcov-1.gcda
bash-4.2$ #в состав NDK входит gcov, который достаточно вызвать, как обычно
bash-4.2$ /users/NDK_current/toolchains/x86-4.7/prebuilt/linux-x86/bin/i686-linux-android-gcov gcov-1.gcda
File ‘gcov-1.c’
Lines executed:100.00% of 6
Creating ‘gcov-1.c.gcov’
bash-4.2$
![Отладка native кода под Android: ручное и автоматизированное тестирование image](https://www.pvsm.ru/images/otladka-native-koda-pod-Android-ruchnoe-i-avtomatizirovannoe-testirovanie-12.png)
Дебаготладка: GDB/logcat
bash-4.2$ #в состав NDK входят так же средства отладки, например, gdb
bash-4.2$ #для удобства отладки бинарники лучше собрать с флагом -g, а так же подгрузить символы
bash-4.2$ /users /NDK_current/toolchains/x86-4.7/prebuilt/linux-x86/bin/i686-linux-android-gcc –sysroot=/users/NDK_current/platforms/android-18/arch-x86 ./hello_world.c -o ./hello_world.exe
bash-4.2$ adb push ./hello_world.exe /data/local/hello_world.exe
bash-4.2$ adb shell gdbserver :5039 /data/local/hello_world.exe &
Process /data/local/hello_world.exe created; pid = 29744
Listening on port 5039
bash-4.2$ adb forward tcp:5039 tcp:5039
bash-4.2$ /users/NDK_current/toolchains/x86-4.7/prebuilt/linux-x86/bin/i686-linux-android-gdb hello_world.exe
Remote debugging from host 127.0.0.1
libthread_db:td_ta_new: Probing system for platform bug.
libthread_db:td_ta_new: Running as root, nothing to do.
Hello, World!
Child exited with status 0
GDBserver exiting
bash-4.2$ cat test.gdb
set sysroot /users/igveresx
set solib-absolute-prefix /users/igveresx/symbols/
set solib-search-path /users/igveresx/symbols/lib
set auto-solib-add on
target remote :5039
stepi
stepi
c
Quit
bash-4.2$ #для отладки и дебаг вывода можно использовать logcat
bash-4.2$ adb logcat *:E >logcat.log &
bash-4.2$ tail -5 logcat.log
E/Intel PowerHAL( 2093): Error in reading vsync hint
E/Intel PowerHAL( 2093): Error reading from /sys/devices/system/cpu/cpufreq/interactive/vsync_count: No such file or directory
E/Intel PowerHAL( 2093): Error in reading vsync count
E/Intel PowerHAL( 2093): Error reading from /sys/devices/system/cpu/cpufreq/interactive/touch_event: No such file or directory
E/Intel PowerHAL( 2093): Error in reading vsync hint
bash-4.2$ #точно так же можно посмотреть сообщения kernel
bash-4.2$ adb shell dmesg | tail -5
<6>[245665.256198] intel_mdf_battery msic_battery: vbus_volt:4974
<6>[245665.265332] intel_mdf_battery msic_battery: vbatt:4116250 temp:300
<4>[245669.213759] kct_daemon: loop.
<4>[245673.213561] kct_daemon: loop.
<4>[245677.213379] kct_daemon: loop.
bash-4.2$ adb shell ‘cat /proc/kmsg’ >kmsg.log
bash-4.2$ tail -5 kmsg.log
<4>[245673.213561] kct_daemon: loop.
<4>[245677.213379] kct_daemon: loop.
<4>[245681.213248] kct_daemon: loop.
<4>[245685.213083] kct_daemon: loop.
<4>[245689.212932] kct_daemon: loop.
bash-4.2$
Автоматизация тестирования
Для автоматизации тестирования можно использовать такой фреймворк, как dejagnu. Начиная с февраля 2013 в состав DejaGnu входит борд androideabi, позволяющий производить тестирование native-кода на Android через adb.
В целом всё аналогично тому, что было описано выше, за исключением некоторых нюансов.
Для многих тестов dejagnu критичны проверки host/target триплетов. Например, как минимум для того, чтобы понять, возможен ли запуск бинарного файла на устройстве, зачастую выполняется проверка host=target=build (native). Однако, в нашем случае, вообще говоря, build_triplet не равен target_triplet, но при этом мы вполне способны и выполнять, и получать результаты на Android. Кроме того, следует учесть тот факт, что по умолчанию NDK использует флажок –fpic, что так же влияет на возможность запуска тестов и их результаты (проверка effective-target pic/nonpic). В случае же статической (static) линковки следует иметь в виду, что, возможно, так же не всё будет соответствовать ожиданиям (библиотеки static, dynamic могут разниться между собой и порождать разный код (-fpic/-fpie). Также некоторые тесты критичны к директории запуска или указанию директории для результатов; и прежде чем запускать бинарный файл, следует сменить директорию на нужную. Кроме того, во время переноса бинарного файла на устройство, права на запуск могут быть сброшены (из-за прав на папку или прав на файловую систему), поэтому так же стоит убедиться, что для исполнимого файла установлен executable bit. Кроме того, лучшим решением для организации тестирования является использование не sd-карты, а ram-диска с правильными правами, чтобы избежать быстрого износа первой.
Чтобы запустить тестирование на Android достаточно выполнить при установленной dejagnu на хосте:
runtest –target_board=androideabi
Следует убедиться, что у вас явным образом указана переменная ADB_SERIAL, соответствующая серийному номеру вашего устройства.
Однако гораздо удобнее и приятнее запускать тестирование через локальный конфигурационный файл — site.exp
Например, конфигурационный файл для запуска gcc testsuite:
set rootme “.”
set tmpdir “.”
set srcdir “/path/to/gcc_%version%_release/gcc/testsuite”
set CFLAGS “”
set CXXFLAGS “”
set GDB “/path/to/GDB_UNDER_TEST”
set GCOV_UNDER_TEST “/path/to/GCOV_UNDER_TEST”
set GCC_UNDER_TEST “/path/to/GCC_UNDER_TEST”
set GXX_UNDER_TEST “/path/to/GXX_UNDER_TEST”
set GFORTRAN_UNDER_TEST “no”
set OBJC_UNDER_TEST “no”
set libiconv “”
set HOSTCC “gcc”
set HOSTCFLAGS “”
set TESTING_IN_BUILD_TREE 1
set GMPINC “”
set ENABLE_LTO 1
set HAVE_LIBSTDCXX_V3 1
set host_triplet i686-pc-linux-gnu
set build_triplet i686-pc-linux-gnu
set target_triplet i686-pc-linux-android-gnu
set target_alias i686-pc-linux-android
set android_tmp_dir “/temporary/folder/on/device/with/executable/permissions”
set bridge_tmp_dir “/temporary/folder/on/device/with/executable/permissions”
append boards_dir “/path/to/share/dejagnu/baseboards”
и для запуска того же самого gcc:
export ADB_SERIAL=$ANDROID_SERIAL
make -j $parallel check DEJAGNU=/path/to/site.exp RUNTESTFLAGS=”–target_board=androideabi”
Замечание: обратите внимание, что следует убедиться, что во время запуска проверки gcc кем-то не будет перетёрта переменная GCC_EXEC_PREFIX, и то, что она unset.
Стоит отметить то, что следует указать не только путь к компилятору, но и следует иметь в виду всё то, что мы делали выше вручную, а именно указать:
- sysroot
- пути к библиотекам и заголовочным файлам
- флаги и подключаемые библиотеки (как минимум для линовки libstdc++)
Исходя из этого, лучшим решением является использование обёртки исполнимого файла из toolchain (wrapper-binaryname), примерно следующего вида:
wrapper-gcc
#!/bin/bash
/path/to/NDK_folder/toolchains/$arch-%compiler_version%/prebuilt/linux-x86/bin/%arch_prefix%-linux-android-gcc –sysroot=/path/to/NDK_folder/platforms/android-${device_platform}/arch-$arch “$@”
wrapper-g++
#!/bin/bash
echo $@ | grep ” -nostdlib” 1>/dev/null 2>/dev/null
if [ $? != 0 ]; then
echo $@ | grep ” -static” 1>/dev/null 2>/dev/null
if [ $? != 0 ]; then
/path/to/NDK_folder/toolchains/$arch-%compiler_version%/prebuilt/linux-x86/bin/%arch_prefix%-linux-android-g++ –sysroot=/path/to/NDK_folder/platforms/android-${device_platform}/arch-$arch -I/path/to/NDK_folder/sources/cxx-stl/%stl lib folder%/%compiler version%/include -I/path/to/NDK_folder/sources/cxx-stl/%stl lib folder%/%compiler version%/libs/$arch/include -L/path/to/NDK_folder/sources/cxx-stl/%stl lib folder%/%compiler version%/libs/$arch “$@” -lgnustl_shared
else
/path/to/NDK_folder/toolchains/$arch-%compiler_version%/prebuilt/linux-x86/bin/%arch_prefix%-linux-android-g++ –sysroot=/path/to/NDK_folder/platforms/android-${device_platform}/arch-$arch -I/path/to/NDK_folder/sources/cxx-stl/%stl lib folder%/%compiler version%/include -I/path/to/NDK_folder/sources/cxx-stl/%stl lib folder%/%compiler version%/libs/$arch/include -L/path/to/NDK_folder/sources/cxx-stl/%stl lib folder%/%compiler version%/libs/$arch “$@” -lgnustl_static
fi
else
/path/to/NDK_folder/toolchains/$arch-%compiler_version%/prebuilt/linux-x86/bin/%arch_prefix%-linux-android-g++ –sysroot=/path/to/NDK_folder/platforms/android-${device_platform}/arch-$arch -I/path/to/NDK_folder/sources/cxx-stl/%stl lib folder%/%compiler version%/include -I/path/to/NDK_folder/sources/cxx-stl/%stl lib folder%/%compiler version%/libs/$arch/include -L/path/to/NDK_folder/sources/cxx-stl/%stl lib folder%/%compiler version%/libs/$arch “$@”
fi
При необходимости, если надо получить какие-то данные из Android, то мы можем использовать функцию remote_upload (adb_upload %target_board% %source% %dest%). Эта функциональность должна предоставляться со стороны testsuite.
Профилирование, тестирование производительности
В рамках данной статьи не будут рассматриваться подробно нюансы сбора и использования профилей, но для Android наиболее распространёнными утилитами для профилирования являются:
- perf
- oprofile
- sep
Например, имея статичную сборку perf, достаточно выполнить:
adb push perf /data/local/
adb shell
cd /data/local
chmod 777 perf
perf record ./coremark.exe 0×0 0×0 0×66 0 7 1 2000 # or any arguments required
# data saved at ./perf.data</li>
export PAGER=cat # otherwise it will look for “less”
perf report
Тестирование производительности на Android возможно производить не только с помощью бенчмарков, работающих через Dalvik, но так же и на native-уровне, т.е. используя бенчмарки, собранные тем же самым Android NDK. В качестве примера: SPEC, EEMBC, CoreMark.
Останавливаться на нюансах портирования фреймворков для Android я не буду (они схожи с описанными выше методами), но стоит заметить, что в основе всего так же лежит принцип использования adb для работы с устройствами или эмуляторами, а так же критичное внимание к процессам, происходящем на устройствах.
Необходимо убедиться, что на результат влияет:
- режим работы процессора
- приложения, запущенные в фоне
- чистота запуска, и ошибки (которые можно и нужно отслеживать через logcat/dmesg)
Релевантные результаты могут быть получены лишь на детерменированных устройствах (если не идёт речь о получении профиля реального использования устройства и приложения). Поэтому, перед запуском необходимо убедиться, что:
- Отключены все второстепенные службы и приложения
- Устройство используется монопольно
- На устройстве жёстко выставлен режим работы
Например, для тестирования CPU типичная практика – это установка всех процессорных ядер и частот в равное значение и их фиксация. При необходимости отключение всех ядер, кроме того, на котором происходит тестирование.
adb shell
echo userspace > /sys/devices/system/cpu/cpuX/cpufreq/scaling_governor
echo 2000000 > /sys/devices/system/cpu/cpuX/cpufreq/scaling_max_freq
echo 2000000 > /sys/devices/system/cpu/cpuX/cpufreq/scaling_min_freq
Следует убедиться, что значение
/sys/devices/system/cpu/cpuX/cpufreq/online так же установлено правильно в зависимости от бенчмаркипрофиля иили потребностей тестирования.
Только убедившись, что всё работает в нужном режиме (cat /proc/cpuinfo) можно начинать тестирование и анализ.
Для упрощения работы и отладки native-кода, для Android существует набор утилит busybox, который не входит в поставку Android по умолчанию (т.к. поставляется по лицензии GPL 2.0).
[, [[, ar, arp, awk, base64, basename, bbconfig, beep, blkid, blockdev, bootchartd, bunzip2, bzcat, bzip2, cal, cat, catv, chat, chattr, chgrp, chmod, chown, chpst, chroot, chrt, chvt, cksum, clear, cmp, comm, cp, cpio, crond, crontab, cttyhack, cut, dc, dd, deallocvt, depmod, devmem, diff, dirname, dmesg, dnsd, dos2unix, dpkg, dpkg-deb, du, dumpkmap, echo, ed, egrep, env, envdir, envuidgid, expand, expr, fakeidentd, false, fbset, fbsplash, fdflush, fdformat, fdisk, fgconsole, fgrep, find, findfs, flash_lock, flash_unlock, flashcp, flock, fold, free, freeramdisk, fsync, ftpd, ftpget, ftpput, fuser, getopt, grep, gunzip, gzip, halt, hd, hdparm, head, hexdump, httpd, hwclock, ifconfig, ifdown, ifup, init, inotifyd, insmod, install, iostat, ip, ipaddr, ipcalc, iplink, iproute, iprule, iptunnel, klogd, less, linuxrc, ln, loadkmap, losetup, lpd, lpq, lpr, ls, lsattr, lsmod, lsof, lspci, lsusb, lzcat, lzma, lzop, lzopcat, makedevs, makemime, man, md5sum, mdev, mesg, mkdir, mkfifo, mknod, mkswap, mktemp, modinfo, modprobe, more, mpstat, mv, nbd-client, nc, netstat, nice, nmeter, nohup, od, openvt, patch, pidof, ping, pipe_progress, pmap, popmaildir, poweroff, powertop, printenv, printf, ps, pscan, pstree, pwd, pwdx, raidautorun, rdev, readlink, readprofile, realpath, reboot, reformime, renice, reset, resize, rev, rm, rmdir, rmmod, route, rpm, rpm2cpio, rtcwake, run-parts, runsv, runsvdir, rx, script, scriptreplay, sed, sendmail, seq, setconsole, setkeycodes, setlogcons, setserial, setsid, setuidgid, sha1sum, sha256sum, sha3sum, sha512sum, showkey, sleep, smemcap, softlimit, sort, split, start-stop-daemon, strings, stty, sum, sv, svlogd, switch_root, sync, sysctl, tac, tail, tar, tcpsvd, tee, telnet, telnetd, test, tftp, tftpd, time, timeout, top, touch, tr, traceroute, true, ttysize, tunctl, tune2fs, udpsvd, uname, uncompress, unexpand, uniq, unix2dos, unlzma, unlzop, unxz, unzip, uptime, usleep, uudecode, uuencode, vconfig, vi, volname, watch, wc, wget, which, whoami, whois, xargs, xz, xzcat, yes, zcat
Использование подхода, описанного в статье, позволяет быстро и легко портировать тестирование из Linux на Android, создавать с нуля и производить отладку для различных конфигураций оборудования и эмуляторов с наименьшими затратами времени.
Автор: wwakabobik