Отладка кода системного уровня Java*/C++ Code для платформы Android*

1 Введение

В настоящее время SDK Android* дает возможность разработчикам приложений создавать проекты приложений Android, собирать их и отлаживать с помощью Eclipse*. Но в этом SDK нет возможности отладки кода Java*/C++ системного уровня. Он содержится в базе кода Android и не может быть собран и отлажен с помощью SDK Android. В этом документе описывается отладка кода Java/C++ системного уровня для платформы Android с помощью Eclipse.

2 Установка

2.1 Установите JDK

Загрузите JDK6 (пакет обновления Java SE 6) по адресу: http://java.sun.com/javase/downloads/index.jsp
Текущая версия JDK6 называется jdk-6u32-linux-x64.bin. Используем ее в качестве примера.

$ cd /usr/lib/jvm $ sudo /path/to/jdk-6u32-linux-x64.bin $ sudo ln -s jdk1.6.0_32 java-6-sun $ export PATH=/usr/lib/jvm/java-6-sun/bin:$PATH $ export JAVA_HOME=/usr/lib/jvm/java-6-sun

2.2 Установите Eclipse

Загрузите Eclipse 3.6.2 или более поздней версии по адресу: http://www.eclipse.org/downloads/
Текущая версия Eclipse Classic - 3.7.2 (Indigo), используем ее в качестве примера.

$ mkdir ~/android-sdk $ cd ~/android-sdk $ tar zxf /path/to/eclipse-SDK-3.7.2-linux-gtk-x86_64.tar.gz

2.3 Установите подключаемые модули Eclipse CDT

Для создания проектов C/C++ и отладки кода C++ нужно установить подключаемые модули CDT.
Запустите Eclipse.

$ cd ~/android-sdk/eclipse/ $ ./eclipse

Если вы работаете в сети, защищенной брандмауэром, настройте параметры прокси-сервера: Window > Preferences > General > Network Connection, как показано на рис. 1.


Рис. 1. Параметры прокси-сервера в Eclipse

Загрузите последнюю версию списка подключаемых модулей Eclipse, щелкнув Help > Check for updates.
Подключите ее через меню Window > Preferences > Install/Update > Available Software Sites.

http://download.eclipse.org/sequoyah/updates/2.0/


Рис. 2. Обновление программного обеспечения Eclipse

Затем установите подключаемые модули CDT: Help > Install New Software.
Выберите “http://download.eclipse.org/sequoyah/updates/2.0/”, снимите флажок Group items by category, затем выберите все подключаемые модули и установите их.


Рис. 3. Установка подключаемых модулей Sequoyah

2.4 Установите SDK Android

Загрузите пакет SDK Android SDK по адресу: http://developer.android.com/sdk/index.htmlТекущая версия SDK Android - r18, будем использовать ее для примера.
Установите SDK Android:

$ cd ~/android-sdk/ $ tar zxf /path/to/android-sdk_r18-linux.tgz

Запустите диспетчер пакетов Android для загрузки пакетов SDK Android:

$ cd ~/android-sdk/android-sdk-linux/tools/ $ ./android

Настройте параметры прокси-сервера и установите флажок “Force https://...sources to be fetched using http://.”


Рис. 4. Параметры прокси-сервера в Android SDK Manager

Затем загрузите нужные пакеты (пакет Tools является обязательным).


Рис. 5. Установка обновлений SDK Android

2.5 Установите подключаемый модуль Android ADT

Установите Android ADT (Android Development Tool).
Запустите Eclipse.

$ cd ~/android-sdk/eclipse/ $ ./eclipse

Щелкните Help->Install New Software, выберите “Add Repository”.
В диалоговом окне Add Repository введите «ADT Plugin» в поле имени и следующий URL-адрес в поле расположения:

https://dl-ssl.google.com/android/eclipse/


Рис. 6. Установка подключаемых модулей ADT

Установите все подключаемые модули ADT, выполнив указанные действия.
Затем откройте меню Window > Preference > Android и введите путь к SDK Android в поле SDK Location.


Рис. 7. Параметры SDK Android

2.6 Установите программу системной отладки Android

Сохраните следующий пакет и разверните его на сервере Linux*.

$ cd ~/android-sdk $ tar zxf android-debug-utility.tar.gz

3 Отладка системного кода Java на платформе Android

3.1 Отладка system_process

Процесс system_process - это так называемый системный сервер Android. От разветвляется при загрузке системы. Сейчас мы увидим, как использовать Eclipse для его отладки.

3.1.1 Создайте псевдопроект Android для system_process

Dalvik Debug Monitor Server (DDMS) может только отлаживать код проектов Android. Процесс system_process собирается из базы кода Android, а не из проекта Android. Чтобы использовать Eclipse/DDMS для отладки system_process, нужно создать псевдопроект Android. Откройте меню File > New > Project и выберите Android Project.


Рис. 8. Создание проекта Android


Рис. 9. Имя пакета Android

Щелкните AndroidManifest.xml и измените имя пакета на system_process


Рис. 10. Измененное имя пакета Android

3.1.2 Отладка system_process с помощью DDMS

Перейдите в DDMS, выберите system_process и режим Debug.


Рис. 11. Представление Android DDMS

Перейдите в представление отладки, выберите любой поток, который нужно отладить, и нажмите кнопку Suspend.


Рис. 12. Представление отладки Android

Для отладки исходного кода Java нужно указать расположение исходного кода Android. Нажмите кнопку Edit Source lookup Path, затем нажмите кнопку Add и выберите File System Directory. В каталогах frameworks/base и libcore содержится код Java платформы Android.

Выберите каталог frameworks/base.


Рис. 13. Настройка пути к каталогу frameworks/base

Также нужно добавить каталог libcore.


Рис. 14. Настройка пути к каталогу libcore

Теперь код Java будет отображен в окне редактора.


Рис. 15. Отладчик проекта Android

Теперь можно задавать точки останова и отлаживать код Java системного уровня для Android.

3.2 Отладка встроенных приложений Android

Существует множество встроенных приложений Android, они также называются системными приложениями. Например, это Календарь, Параметры, Настройки и т.п. Эти проекты находятся в базе кода Android и входят в состав system.img. Для их отладки нужно создавать псевдопроекты, как мы сделали для system_process. Но вместо имя пакета нужно изменить не на system_process, а на имя приложения.


Рис. 16. Имя пакета приложения Android

Перейдите в DDMS, выберите процесс com.android.calendar и нажмите Debug.


Рис. 17. Выбор приложения для отладки


Рис. 18. Отладчик приложений Android

4 Отладка системного кода C/C++ на платформе Android

4.1 Отладка system_process

Как правило, для отладки кода C/C++ для платформы Android можно использовать gdbserver/gdb. Следует использовать gdbserver для присоединения к запущенному процессу, а затем gdb для его удаленной отладки. Однако процесс system_ process создается при загрузке системы, инициализация осуществляется до того, как появляется возможность присоединения к этому процессу. Для отладки system_process с самого начала мы будем использовать программу android-debug-utility, помещающую system_process в бесконечный цикл с самого начала, благодаря чему у нас появляется возможность присоединения к этому процессу на самом раннем этапе. После этого мы устанавливаем для system_process выход из бесконечного цикла и продолжаем работу.

4.1.1 Создайте проект C/C++

Можно создать новый проект C/C++. Также можно преобразовать существующий проект Android «system_process» в проект C/C++. Щелкните File > New > Convert to a C/C++ Project (Adds C/C++ Nature), выберите проект system_process:


Рис. 19. Преобразование проекта Android в проект C/C++

4.1.2 Создайте конфигурацию отладки

Перейдите в представление отладки, нажмите Debug, щелкните Debug Configurations, создайте конфигурацию удаленной отладки приложения C/C++, введите путь к двоичному файл app_process и отключите автоматическую сборку.


Рис. 20. Настройка образа приложения для отладки

Перейдите в каталог android-debug-utility, измените сценарий инициализации gdb sdk_x86_gdb.setup, замените $android и $__board на корневой каталог Android и имя устройства, чтобы предоставить для gdb правильные каталоги с символьными файлами:

shell echo set solib-absolute-prefix $android/out/target/product/$__board/symbols > tmp.gdb shell echo set solib-search-path $android/out/target/product/$__board/symbols/system/lib:$__board/out/target/product/$__board/symbols/system/lib/hw:$__board/out/target/product/$__board/symbols/system/lib/egl:$__board/out/target/product/$__board/symbols/system/lib/soundfx:$__board/out/target/product/$__board/symbols/system/lib/bluez-plugin:$__board/out/target/product/$__board/symbols/system/vendor/lib/egl:$__board/out/target/product/$__board/symbols/system/vendor/lib/hw:$__board/out/target/product/$__board/symbols/system/vendor/lib:$__board/out/target/product/$__board/symbols/system/lib/parameter-framework-plugins/Audio:$__board/out/target/product/$__board/symbols/system/lib/soundfx:$__board/out/target/product/$__board/symbols/system/usr/lib/alsa-lib >> tmp.gdb source tmp.gdb

Также можно просто экспортировать две переменных среды перед запуском Eclipse:

$ export android= $ export __board=

Перейдите на вкладку Debugger и введите путь к sdk_x86_gdb.setup:


Рис. 21. Настройка сценария инициализации gdb

Перейдите на вкладку Connection и измените номер порта на 1234.


Рис. 22. Настройка порта отладки gdb

4.1.3 Отладка system_process

Запустите устройство Android и подключение его кабелем USB к серверу. Перезапустите system_process и используйте gdbserver для присоединения к нему:

$ cd ~/android-sdk/android-debug-utility/target $ ./start_system_server.sh zygote:115 killall: gdbserver: no process killed killall: gdb: no process killed /lib/gdbserver: No such file or directory mkdir failed for /lib, File exists push: ./lib/ld-linux.so.2 -> /lib/ld-linux.so.2 push: ./lib/system_server -> /lib/system_server push: ./lib/libthread_db.so.1 -> /lib/libthread_db.so.1 push: ./lib/libpthread.so.0 -> /lib/libpthread.so.0 push: ./lib/libc.so.6 -> /lib/libc.so.6 push: ./lib/libreadline.so.5.2 -> /lib/libreadline.so.5.2 push: ./lib/libreadline.so.6 -> /lib/libreadline.so.6 push: ./lib/gdb -> /lib/gdb push: ./lib/libdl.so.2 -> /lib/libdl.so.2 push: ./lib/app -> /lib/app push: ./lib/libz.so.1 -> /lib/libz.so.1 push: ./lib/libm.so.6 -> /lib/libm.so.6 push: ./lib/libutil.so.1 -> /lib/libutil.so.1 push: ./lib/libexpat.so.1 -> /lib/libexpat.so.1 push: ./lib/libpython2.6.so.1.0 -> /lib/libpython2.6.so.1.0 push: ./lib/libcrypto.so.0.9.8 -> /lib/libcrypto.so.0.9.8 push: ./lib/service -> /lib/service push: ./lib/libssl.so.0.9.8 -> /lib/libssl.so.0.9.8 push: ./lib/gdbserver -> /lib/gdbserver push: ./lib/libncurses.so.5 -> /lib/libncurses.so.5 20 files pushed. 0 files skipped. 1770 KB/s (10526293 bytes in 5.805s) 4 KB/s (440 bytes in 0.099s) please connect to the system server via gdb remote Attached; pid = 309 Listening on port 1234

Перенаправьте локальный TCP-порт 1234 на устройство Android с помощью adb:

$ adb forward tcp:1234 tcp:1234

В среде Eclipse перейдите в представление отладки:
Нажмите Debug, выберите Debug Configurations, выберите конфигурацию system_process_1234, затем нажмите кнопку Debug.


Рис. 23. Запуск отладчика C/C++

Дождитесь подключения gdb к gdbserver:


Рис. 24. Отладчик C/C++ system_server

Приостановите задачу, перейдите в консоль и введите команды «thread 1» и «go», которые выведут задачу из бесконечного цикла.

thread 1 [Switching to thread 1 (Thread 309)] #0 setgid () at bionic/libc/arch-x86/syscalls/setgid.S:10 10 pushl %ebx go system server in dead loop, trying to restore...done

Задайте точки останова и приступите к отладке кода.

4.2 Отладка приложений Android

Как и system_process, приложения Android также разветвляются. Их невозможно запустить как обычную программу C/C++ с основной функцией. Для отладки приложения Android его необходимо с самого начала поместить в бесконечный цикл, как мы сделали с system_process, чтобы можно было как можно раньше присоединить gdbserver. Затем нужно создать проект C/C++ и конфигурацию отладки для него. Для удобства мы снова используем проект C/C++ system_process и конфигурацию отладки system_process_1234.

4.2.1 Отладка приложения Android

Запустите устройство Android и подключение его кабелем USB к серверу. Выполните следующий сценарий start_app.sh:

$ cd ~/android-sdk/android-debug-utility/target $ ./start_app.sh zygote:305 killall: gdbserver: no process killed please launch the app

Запустите приложение Android с помощью средства запуска Android или из Eclipse. Появится сообщение о возможности подключиться к приложению.

$ ./start_app.sh zygote:305 killall: gdbserver: no process killed please launch the app please connect to the app 1362 via gdb remote Attached; pid = 1362 Listening on port 1234

В Eclipse перейдите в представление отладки.
Нажмите Debug, выберите Debug Configurations, выберите конфигурацию system_process_1234, затем нажмите кнопку Debug


Рис. 25. Запуск отладчика C/C++

Дождитесь подключения gdb к gdbserver.


Рис. 26. Отладчик C/C++ приложения Android

Приостановите задачу, перейдите в консоль и введите команды «thread 1» и «go»:

[New Thread 1374] [Switching to Thread 1362] thread 1 [Switching to thread 1 (Thread 1362)] #0 0xb75cb343 in android::IPCThreadState::clearCallingIdentity (this=0x983d580) at frameworks/base/libs/binder/IPCThreadState.cpp:375 375 { go android app in dead loop, trying to restore...done

Задайте точки останова и приступите к отладке кода.

4.3 Отладка служб Android

В сценарии инициализации Android init.rc следующие встроенные службы запускаются при загрузке системы:

service mtpd /system/bin/mtpd class main socket mtpd stream 600 system system user vpn group vpn net_admin inet net_raw disabled oneshot service keystore /system/bin/keystore /data/misc/keystore class main user keystore group keystore socket keystore stream 666

Обычно эти службы написаны на C/C++, их вхождение - main(). Но эти службы запускаются в процессе инициализации, и их поведение может отличаться при запуске вручную. Для отладки этих служб используем сценарий start_service.sh. Также для них нужно создать проекты C/C++ и конфигурации отладки. Для удобства мы снова используем существующий проект system_process и конфигурацию отладки.

4.3.1 Отладка служб Android

Использование: start_service.sh [ service name ]
На сервере введите:

$ cd ~/android-sdk/android-debug-utility/target $ ./start_service.sh mtpd init:1 killall: gdbserver: no process killed killall: gdb: no process killed 5 KB/s (585 bytes in 0.099s) please start the service: start mtpd

На целевом устройстве введите:

# start mtpd

На сервере появится сообщение о том, что можно приступить к отладке службы.

user@ubuntu:~/android-sdk/android-debug-utility/target$ ./start_service.sh mtpd init:1 killall: gdbserver: no process killed killall: gdb: no process killed 5 KB/s (585 bytes in 0.099s) please start the service: start mtpd please connect to the app 1561 via gdb remote

В отличие от прочих Java-приложений Android, службы представляют собой автономные приложения C/C++ и не используют совместно образ app_process, поэтому нужно изменить конфигурацию отладки, чтобы указать, где находится двоичный код службы.


Рис. 27. Настройка двоичного кода службы Android для отладки

Затем запускаем отладчик с конфигурацией system_process_1234 и дождемся подключения отладчика к gdbserver.


Рис. 28. Отладчик службы C/C++ Android

Приостановите задачу, перейдите в консоль и введите команды «thread 1» и «go».

thread 1 [Switching to thread 1 (Thread 1561)] #0 0x08049663 in main (argc=1, argv=0xbf9a3d64) at external/mtpd/mtpd.c:155 155 { go android service in dead loop, trying to restore...done

Теперь можно задать точки останова и приступить к отладке.

5 Гибридная отладка Java и C/C++

5.1 Трассировка вызовов из Java к встроенному коду

Иногда требуется трассировка всех вызовов из Java к встроенному коду. Для этого потребуются обе методики отладки Java и C/C++, описанные выше.

  1. Используйте script start_system_server.sh или start_app.sh, чтобы поместить system_process и приложение в бесконечный цикл.
  2. Присоединитесь к system_process или к приложению с помощью DDMS.
  3. Выведите system_process или приложение из бесконечного цикла с помощью команды «go».
  4. Переключите точку останова C/C++ в нужной встроенной функции.
  5. Поместите встроенную функцию в бесконечный цикл с помощью команды «loop».
  6. Запустите ее.
  7. Перейдите в отладчик Android, остановите потоки и проверьте стеки вызовов каждого потока. Выявите поток, вызывающий встроенную функцию (находящуюся в бесконечном цикле).

Теперь объединим трассировку вызова Java и вызова C/C++, чтобы отследить весь вызов из Java во встроенный код. Вот трассировка вызова в приведенном ниже случае:
Java: WindowManagerService$Session.add-> SurfaceSession.init()->
C/C++: SurfaceSession_init()->android::SurfaceFlinger::createConnection()

system_process_1234 [C/C++ Remote Application] app_process [cores: 0,1] Thread [52] 10969 [core: 1] (Suspended : Container) Thread [51] 10968 [core: 1] (Suspended : Container) Thread [50] 10556 [core: 1] (Suspended : Container) Thread [49] 10547 [core: 1] (Suspended : Container) android::SurfaceFlinger::createConnection() at SurfaceFlinger.cpp:174 0x8643b728 android::SurfaceComposerClient::onFirstRef() at SurfaceComposerClient.cpp:170 0x81f248b9 android::RefBase::incStrong() at RefBase.cpp:304 0x8051e128 sp() at RefBase.h:353 0x808711f3 SurfaceSession_init() at android_view_Surface.cpp:109 0x808711f3 dvmPlatformInvoke() at Call386ABI.S:133 0x8302be8f dvmCallJNIMethod_virtualNoRef() at Jni.c:1,849 0x8307505b dvmCheckCallJNIMethod_virtualNoRef() at CheckJni.c:158 0x83052741 dvmInterpretDbg() at InterpC-portdbg.c:4,354 0x8304083d dvmInterpret() at Interp.c:1,335 0x83036521 <...more frames...> <== system_process [Android Application] DalvikVM[localhost:8600] Thread [<1> main] (Running) Thread [<50> Binder Thread #10] (Running) Thread [<49> Binder Thread #9] (Running) Thread [<47> Binder Thread #8] (Running) Thread [<44> Binder Thread #7] (Suspended) SurfaceSession.init() line: not available [native method] SurfaceSession.() line: 29 WindowManagerService$Session.windowAddedLocked() line: 5774 WindowManagerService$WindowState.attach() line: 6107 WindowManagerService.addWindow(WindowManagerService$Session, IWindow, WindowManager$LayoutParams, int, Rect, InputChannel) line: 1914 WindowManagerService$Session.add(IWindow, WindowManager$LayoutParams, int, Rect, InputChannel) line: 5664 WindowManagerService$Session(IWindowSession$Stub).onTransact(int, Parcel, Parcel, int) line: 68 WindowManagerService$Session.onTransact(int, Parcel, Parcel, int) line: 5636 WindowManagerService$Session(Binder).execTransact(int, int, int, int) line: 320 NativeStart.run() line: not available [native method]

5.2 Отслеживание трассировки вызовов IPC

Взаимодействие между процессами (IPC) в Android реализовано с помощью связки. Одна задача вызывает одну функцию, которая может быть выполнена другой задачей и получает результат из выполняющейся задачи. Иногда нужно отследить всю трассировку вызовов IPC. К счастью, связка IPC работает синхронно. Это означает, что если выполнение вызванной задачи не завершено, вызвавшая задача не будет возвращена. Если поместить бесконечный цикл в вызванную задачу, вызвавшая задача будет заблокирована в вызове IPC. Это дает нам возможность отслеживать трассировку и вызванной, и вызвавшей задач.

Для отслеживания обеих задач нужно запустить два сеанса gdbserver/gdb, по одному для каждой задачи. Можно настроить еще один проект C/C++ system_process_2 и еще одну конфигурацию отладки с TCP-портом отладчика 1235.

  1. Используйте действия, перечисленные в п. 5.1, для подключения к первой задаче с помощью конфигурации отладки system_process_1234.
  2. Используйте такие же действия для подключения ко второй задаче с помощью конфигурации отладки system_process_1235.
  3. Примечание. Нужно изменить start_app.sh, чтобы отладчик прослушивал 1235, а не 1234, и использовать adb для перенаправления локального порта 1235 на устройство.
  4. Поместите вызванную задачу в бесконечный цикл или просто задайте одну точку останова в нужной функции и выполняйте задачу, пока она не остановится.
  5. Отследите трассировку обеих задач.

Теперь можно отследить всю трассировку от вызывающей задачи к вызываемой задаче. Если необходимо, можно подсоединиться к их отладчику Java, чтобы также отслеживать трассировку Java во всех вызовах IPC.

For more complete information about compiler optimizations, see our Optimization Notice.