文中所使用的例子:https://github.com/dodola/BinderDebug
最近在研究Binder架構,由于本人比較菜,只分析代碼邏輯無法很清楚的了解其中的數據流向以及數據結構,所以想整理一套簡單的調試工具幫助我來分析,我一共嘗試過三種方法:
- 直接使用GDB調試(本人比較菜用的很不順手)
- Eclipse+GDB,效果很好,配置起來有點復雜
- AndroidStudio+LLDB,這是用著效果最好
先來看一下我們可以調試到什么程度
從上圖中可以看到調試IPCThreadState.cpp的過程,我們可以看到參數的值,以及各個變量的結構和對應的值
下面描述一下環境搭建過程.
注:本人系統環境是Mac
編譯系統源碼
進行調試的前提是我們有一套系統源碼,并且已經編譯完成
我編譯了三個版本的代碼:4.4.1_r1
(本文使用的版本),5.1.0_r1
,6.0.1_r1
,經我測試都可以使用這種方法調試
這里不寫編譯過程了,具體可以查看官方文檔
如果沒有更改編譯輸出目錄的話,編譯完成后會在源碼根目錄生成out
文件夾
我們需要關注的是out/target/product/generic
下的東西:
配置Android Studio NDK編譯環境
Build Environment
Android Studio 2.1 Preview 4
Gradle 2.10
Android NDK r10e
gradle-experimental 0.7.0-alpha1
基本上都是最新的環境.
編譯Demo例子
Android Studio 1.4以后就支持NDK編譯了,詳細的Gradle配置可以去看The new NDK support in Android Studio這篇文章,這里不在贅述,想嘗試例子的可以去google 官方的github,直接導入運行即可
這里只描述一下如何使用NDK編譯依賴系統庫的應用.
我們都知道NDK里并沒有提供Binder庫的相關頭文件和lib庫,所以無法直接使用NDK編譯Binder C++ 的程序,所以我們需要從編譯好的系統目錄里將需要的共享庫提取出來供我們編譯使用.
下面是我提取出來編譯該Demo所使用的shared libs:
libbinder.so
是Binder的核心庫,包含ServiceManager和Pracple的代碼,其他三個都是libbinder.so的依賴庫
除此之外還需要這些庫的頭文件,目錄樹如下:
.
├── binder ----> libbinder.so
├── core ---->別的頭文件里有引用此頭文件
│ └── include
│ ├── android
│ ├── corkscrew
│ ├── ctest
│ ├── cutils
│ ├── diskconfig
│ ├── ion
│ ├── log
│ ├── memtrack
│ ├── mincrypt
│ ├── netutils
│ ├── pixelflinger
│ ├── private
│ │ └── pixelflinger
│ ├── sync
│ ├── system
│ ├── sysutils
│ ├── usbhost
│ ├── utils
│ └── zipfile
├── cutils ---->libcutils.so
├── libc ----->暫時沒用到
│ ├── android
│ ├── arpa
│ ├── machine
│ ├── net
│ ├── netinet
│ ├── netpacket
│ └── sys
├── log
├── system
└── utils ---->libutils.so
下面貼出gradle配置文件里系統庫的配置
apply plugin: 'com.android.model.application'
model {
repositories {
libs(PrebuiltLibraries) {
libc {
headers.srcDir rootDir.absolutePath + "/binder_lib/include/libc"
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file(rootDir.absolutePath + "/binder_lib/libs/libc.so")
}
}
binderlib {
headers.srcDir rootDir.absolutePath + "/binder_lib/include"
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file(rootDir.absolutePath + "/binder_lib/libs/libbinder.so")
}
}
libcutils {
headers.srcDir rootDir.absolutePath + "/binder_lib/include/core/include"
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file(rootDir.absolutePath + "/binder_lib/libs/libcutils.so")
}
}
libutils {
headers.srcDir rootDir.absolutePath + "/binder_lib/include/core/include"
binaries.withType(SharedLibraryBinary) {
sharedLibraryFile = file(rootDir.absolutePath + "/binder_lib/libs/libutils.so")
}
}
}
}
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.2"
println(rootDir.absolutePath + "/binder_lib/include/libc")
defaultConfig {
applicationId "dodola.binder"
minSdkVersion.apiLevel 15
targetSdkVersion.apiLevel 22
versionCode 1
versionName "1.0"
buildConfigFields {
create() {
type "int"
name "VALUE"
value "1"
}
}
}
}
android.ndk {
moduleName = "binder_ndk"
cppFlags.addAll(["-Werror", "-fno-rtti", "-fno-exceptions"/*, "-std=c++11"*/])
CFlags.addAll(["-Werror"])
ldLibs.addAll(['android', 'log'])
// stl = "gnustl_static"
debuggable = true
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file('proguard-rules.txt'))
}
}
android.sources {
main {
jni {
dependencies {
// library "libc" linkage "shared"
library "binderlib" linkage "shared" buildType "debug"
library "libcutils" linkage "shared" buildType "debug"
library "libutils" linkage "shared" buildType "debug"
}
}
}
}
android.productFlavors {
// for detailed abiFilter descriptions, refer to "Supported ABIs" @
// https://developer.android.com/ndk/guides/abis.html#sa
create("arm") {
ndk.abiFilters.add("armeabi")
}
create("all")
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.2.1'
}
Android Studio 配置
然后添加一個 Android Native 的Configuration
Module這里選擇 APP
然后在Debugger 里,添加Symbol 文件夾(不確定是否有用)
運行應用
需要注意的是這個例子需要在自己編譯好的系統下運行,因為我們使用的so文件在調試的時候需要根據symbol的地址查找源碼,否則就會出現地址找不到的情況.
我沒有真機進行調試這里只演示模擬器的調試.
在已經經過編譯的系統源碼目錄下運行以下命令
> $ . build/envsetup.sh ? 5.7.0
build/envsetup.sh:537: command not found: complete
WARNING: Only bash is supported, use of other shell would lead to erroneous results
including device/asus/deb/vendorsetup.sh
including device/asus/flo/vendorsetup.sh
including device/asus/grouper/vendorsetup.sh
including device/asus/tilapia/vendorsetup.sh
including device/generic/armv7-a-neon/vendorsetup.sh
including device/generic/mips/vendorsetup.sh
including device/generic/x86/vendorsetup.sh
including device/lge/hammerhead/vendorsetup.sh
including device/lge/mako/vendorsetup.sh
including device/samsung/manta/vendorsetup.sh
> $ lunch ? 5.7.0
You're building on Darwin
Lunch menu... pick a combo:
1. aosp_arm-eng
2. aosp_x86-eng
3. aosp_mips-eng
4. vbox_x86-eng
5. aosp_deb-userdebug
6. aosp_flo-userdebug
7. aosp_grouper-userdebug
8. aosp_tilapia-userdebug
9. mini_armv7a_neon-userdebug
10. mini_mips-userdebug
11. mini_x86-userdebug
12. aosp_hammerhead-userdebug
13. aosp_mako-userdebug
14. aosp_manta-userdebug
Which would you like? [aosp_arm-eng]
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.4.1
TARGET_PRODUCT=aosp_arm
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a
TARGET_CPU_VARIANT=generic
HOST_ARCH=x86
HOST_OS=darwin
HOST_OS_EXTRA=Darwin-15.4.0-x86_64-i386-64bit
HOST_BUILD_TYPE=release
BUILD_ID=KOT49E
OUT_DIR=out
============================================
> $ emulator ? 5.7.0
emulator: WARNING: system partition size adjusted to match image file (550 MB > 200 MB)
這樣就可以將模擬器啟動起來
然后就可以在Android Studio里運行例子了.
調試應用
上面所有步驟配置好以后,則可以調試應用了
- 選擇
Attach debug to Android process
- 在彈出的對話框里選擇
Native
選項
- 點擊ok以后,debug的控制臺會輸出LLDB的調用命令
效果
哈,這樣我們在調試的時候就可以深入到binder內部看一下它的流程了
下面這個圖是斷點到IServiceManager.cpp的情況,以及變量的結構
下面這個圖是mData里的結構(以前只能靠YY)