Android FrameWork學習(二)Android系統原始碼除錯

香辣牛肉麵發表於2019-02-25

通過上一篇 Android FrameWork學習(一)Android 7.0系統原始碼下載\編譯 我們瞭解瞭如何進行系統原始碼的下載和編譯工作。

為了更進一步地學習跟研究 Android 系統原始碼,今天我們來講講如何進行 Android 系統原始碼的除錯,只有學會了如何進行系統原始碼的除錯,才能幫助我們更高效地閱讀跟理解原始碼。

我們知道,Android Framework 的程式碼主要由Java、C\C++等程式碼組成,因此,對於系統原始碼的除錯,我們這裡將其分為了兩部分

  • Java 相關程式碼的除錯
  • C\C++ Native 相關程式碼的除錯

#一、Java 相關程式碼的除錯

對於 Java 相關程式碼的除錯,這裡我們主要使用 Android Studio 開發工具來進行。

###匯入原始碼到 Android Studio 要在 Android Studio 中除錯原始碼,那第一步自然是匯入系統原始碼到 Android Studio 中了。

####1. 編譯 idegen 對於 Android 原始碼的匯入, Google 官方給我們提供了一個很方便的工具 idegen

它位於我們所下載的系統原始碼路徑中:

developement/tools/idegen
複製程式碼

引用 README 的一句話

IDEGen automatically generates Android IDE configurations for IntelliJ IDEA and Eclipse.

idegen 工具會自動生成針對 Android 開發工具(Android Studio和Eclipse)的配置檔案。

既然如此,那我們就來使用 idegen 工具生成匯入原始碼所需的配置檔案。

首先開啟命令列工具,cd 進入到原始碼路徑下,

執行如下指令:

#初始化命令工具
soruce build/envsetup.sh 
#編譯 idegen 模組,生成idegen.jar
mmm development/tools/idegen/
#生成針對 Android 開發工具的配置檔案 
sudo ./development/tools/idegen/idegen.sh
複製程式碼

在執行完上述指令後,會在原始碼路徑下生成下面三個檔案

Paste_Image.png

android.ipr:工程相關的設定,比如編譯器配置、入口,相關的libraries等。

android.iml:描述了modules,比如modules的路徑,依賴關係等。

android.iws:包含了一些個人工作區的設定。

####2. 匯入原始碼 接下來我們可以開始匯入原始碼了.

如果你是第一次匯入原始碼, Android Studio 可能需要佔用大量的記憶體,我們需要設定下我們的 VM 選項。

Linux 裝置的話在 Android Studio 的 bin/studio64.vmoptions 檔案中新增-Xms748m -Xmx748m

如果你使用的是 Mac ,那麼在 AS 目錄的 Contents/Info.plist 目錄中進行新增。

由於 Android 的系統原始碼非常龐大,一次性匯入 Android Studio 的話需要載入非常長的時間

因此,在正式開始匯入前,我們可以開啟 android.iml 檔案根據自己需要調整要載入的原始碼。

Paste_Image.png

這裡 <excludeFolder> 表示不需要載入的目錄,我們根據自己的需要使用 <excludeFolder> 標籤新增對應的目錄地址即可。

接著,選擇 File -> open 選中 android.ipr 檔案,開啟

Paste_Image.png

這時 Android Studio 就會開始載入原始碼了

在沒有新增修改 <excludeFolder> 的情況下,這個載入的時間會比較長,經過一段時間的等待後,程式碼就載入完畢了,如圖:

Paste_Image.png

這裡紅色的目錄代表被 exclude 排除了,程式碼載入 scan index 的時候會過濾掉該目錄。

在載入完原始碼後,我們也可以在 Project Structure 中的 Module 選項中右鍵 exclude 來排除不需要載入的原始碼目錄,如圖:

Paste_Image.png

Paste_Image.png

####3. 配置程式碼依賴,確保程式碼跳轉正確

為了閱讀和除錯程式碼的時候能夠保證程式碼跳轉正確,我們需要配置下相關依賴。

首先是 AOSP 原始碼的跳轉,我們通過 File -> Project Structure 開啟 Module,然後選中 Dependencies, 保留 JDK 跟 Module Source 項,並新增原始碼的 external 和 frameworks 依賴,如圖:

Paste_Image.png

然後是 SDK 的設定,確保關聯對應版本的 SDK 於系統版本一直

Paste_Image.png

###開始除錯原始碼

除錯前要設定 Project 的 SDK , File -> Project 下開啟 Project Structure,選中 Project 設定對應版本的 SDK,於系統版本一致:

Paste_Image.png

確保 Android Studio 允許 ADB 除錯

Paste_Image.png

接著我們參照上一篇文章中講的方法開啟 Android 模擬器

此時點選 Android Studio 工具欄的 attach debugger to Android process 按鈕,會開啟 Choose Process 視窗,我們根據自己需要除錯的程式碼選擇對應的程式:

Paste_Image.png

這裡假設我們要除錯 Android 自帶瀏覽器的原始碼,如圖,我們在它的入口檔案 WebViewBrowserActivity 中的 loadUrlFromUrlBar 方法中打上斷點。

Paste_Image.png

點選 WebViewBrowser 開啟 app

Paste_Image.png

開啟之後,點選 attach to Android process 按鈕開啟 choose Process,可以看到 webViewBrowser 執行的程式,選中,ok

Paste_Image.png

然後我們在 app 的 url 輸入欄輸入 網址進行跳轉

Paste_Image.png

Paste_Image.png

如圖所示我們可以看到,程式碼成功進入了斷點,然後我們就可以隨心所欲地除錯我們想要的除錯的 Java 程式碼了。


#二、Native C\C++ 相關程式碼除錯 對於 Framework Native 程式碼,我們這裡使用 GDB 工具來進行除錯。

###什麼是 GDB 呢? 它是一款 GNU 專案除錯工具,它的功能非常強大,可以用來除錯 C 、C++、Object-C、Pascal 等語言編寫的專案。

對於使用習慣了視覺化 IDE 的同學們來說,它最大的缺點可能就是它不支援圖形化了

但是 GDB 提供的指令非常靈活,通過指令我們

  • 可以隨心所欲地啟動程式,
  • 可以根據自己的需要設定斷點,
  • 可以檢視斷點處的變數,程式碼資訊
  • 可以檢視程式執行的呼叫棧

一旦你熟悉了它,你便可以玩得飛起!

一般情況下,使用 gdb 來除錯 Android 原始碼需要在 Android 裝置上安裝 gbdserver attach 關聯我們需要除錯的程式,再使用 gdb 指令去連線 gdbserver 進行除錯

不過官方給我們提供了 gdbclient 工具,可以讓我們方便地進行 gdb 除錯。

###開始 GDB 除錯

這裡我們就基於 gdbclient 來進行實際的 gdb 除錯演示:

跟上面 Java 除錯一樣,我們這裡還是以系統自帶的瀏覽器為例。

####1. 點選啟動圖示開啟瀏覽器 app:

Paste_Image.png

####2. 開啟一個命令列終端,cd 進入到系統原始碼目錄(我的原始碼路徑為 aosp),初始化命令工具:

#進入原始碼路徑
cd aosp
#初始化命令工具
source build/envsetup.sh
#選擇編譯的原始碼的版本,參考上一篇文章
lunch
複製程式碼

初始化命令工具

Paste_Image.png

####3. 通過 adb 指令來查詢要除錯程式的 PID

# 通過 shell ps 指令查詢相關程式,grep 搜尋過濾 webview 程式
adb shell ps -A | grep webview
複製程式碼

Paste_Image.png

如圖,2157 為系統自帶瀏覽器 app 所在程式的 PID

####4. 使用 gdbclient 命令工具啟用 gdb 除錯 PID 對應程式

# gdbclient <app pid> 可以啟用 gdb 除錯對應 PID 程式
gdbclient 2157
複製程式碼

Paste_Image.png

等待進入 gdb 除錯命令介面

Paste_Image.png

####5. 使用 GDB b 命令打斷點

在 gdb 指令中,我們使用b <程式碼檔案>:行號 來設定斷點.

這裡我們選擇 frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp 程式碼檔案的 drawFrame 方法打上斷點,位於檔案 71 行:

Paste_Image.png

該方法主要用於繪製幀,當瀏覽器 app 的介面發生變化時會觸發該方法。

我們輸入設定斷點命令:

b frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp:71
複製程式碼

Paste_Image.png

輸入指令後顯示 Breakpoint 2 at 0x7f69e9892c11: file frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp, line 71. 說明我們的斷點設定成功了。

####6. 在命令列輸入c 開始監聽

Paste_Image.png

c 即 continue,此時介面上出現 Continuing 說明開始監聽程式了

我們點開模擬器,隨意操作,觸發介面變化時,便會進入繪製幀的程式碼斷點了:

Paste_Image.png

如圖,顯示進入斷點,這樣代表我們的程式碼除錯成功了。


這裡我們只是演示了一個大概流程,

gdb 程式碼的除錯需要你對原始碼有一定的熟悉,知道哪個程式會呼叫哪個檔案方法。

同時,我們還需要熟悉 gdb 的各種命令,這裡給大家推薦一篇不錯的入門文章,可以快速入門:

GDB十分鐘教程

這裡補充一點,如果你希望在某個程式啟動時就監聽,可以使用下面的指令關聯目錄,得到 pid,再通過 gdbclient 來進行除錯

adb shell gdbserver :5039 /system/bin/my_test_app
Process my_test_app created; pid = 3460
Listening on port 5039
複製程式碼
gdbclient <app pid>
複製程式碼

如果你希望通過 Android Studio 來除錯 Framework 的 C\C++ 程式碼的話,也可以參考下面的兩篇文章,不過個人覺得這種方法有一定的侷限性。

如何除錯Android Native Framework

用Android Studio除錯Framework層程式碼


#結語 正如文章開頭所說,只有學會了如何除錯 Framework 原始碼,才能幫助我們更好地學習 Android Framework,希望這篇文章能給大家一些幫助,如果有更好地除錯方法,歡迎大家給我留言咯!

相關文章