1. 程式人生 > >Android FrameWork 系統原始碼除錯

Android FrameWork 系統原始碼除錯

這是很久以前訪問掘金的時候 無意間看到的一個關於Android的文章,作者更細心,分階段的將學習步驟記錄在自己部落格中,我覺得很有用,想作為分享同時也是留下自己知識的一些欠缺收藏起來,今後做專案的時候會用到。

好了,廢話不多說了。直接來吧。。

這是Android 7.0系統原始碼下載\編譯

原文

最近計劃著研究下Android 7.0的系統原始碼,之前也沒做過什麼記錄,這次正好將學習的內容記錄下來,方便以後複習鞏固。

既然要學習我們的系統原始碼,那我們第一步要做的就是下載原始碼並進行編譯了。

硬體環境要求

1. 編譯環境

按照官方的說法,編譯Android 2.3.x及以上版本的系統原始碼需要64位的系統執行環境來支援,而編譯2.3.x以下的版本則需要32位的系統執行環境。

2. 硬碟空間

官方建議最好預留100G的磁碟空間來下載原始碼,150G的磁碟空間用來編譯原始碼,如果使用了ccache(一個高速編譯快取工具,可以大幅加快gcc的編譯速度),那麼則需要更大的空間來支援。

所以儘可能地保證自己的磁碟空間夠大吧,之前就因為磁碟空間預留不夠導致原始碼編譯過程中空間不足,狠狠地把自己坑了一把。

3. 記憶體空間

如果你是在虛擬機器上跑Linux,官方建議至少需要16G的記憶體空間,我的機器只有8G的記憶體空間跑虛擬機器,目前跑起來也沒太大問題,就是編譯原始碼的過程非常漫長,不知道是否跟記憶體大小有關。

軟體環境要求

1. 作業系統

Android系統的原始碼的編譯支援Linux跟Mac OS兩種作業系統

,一般情況下,Android系統原始碼都是在Linux Ubuntu系統上進行開發與測試的,所以如果你準備使用Linux系統來進行原始碼編譯,那一般推薦安裝Ubuntu版本的Linux。

下面列出了各Android版本與編譯系統版本的對應關係

Linux:

Android版本 GNU/Linux
Android 6.0 (Marshmallow) - Android最新版本 Ubuntu 14.04 (Trusty)
Android 2.3.x (Gingerbread) - Android 5.x (Lollipop) Ubuntu 12.04 (Precise)
Android 1.5 (Cupcake) - Android 2.2.x (Froyo) Ubuntu 10.04 (Lucid)

Mac OS

Android版本 Mac OS (Intel/x86)
Android 6.0 (Marshmallow) - Android最新版本 Mac OS v10.10 (Yosemite) or later with Xcode 4.5.2 and Command Line Tools
Android 5.x (Lollipop) Mac OS v10.8 (Mountain Lion) with Xcode 4.5.2 and Command Line Tools
Android 4.1.x-4.3.x (Jelly Bean) - Android 4.4.x (KitKat) Mac OS v10.6 (Snow Leopard) or Mac OS X v10.7 (Lion) and Xcode 4.2 (Apple’s Developer Tools)
Android 1.5 (Cupcake) - Android 4.0.x (Ice Cream Sandwich) Mac OS v10.5 (Leopard) or Mac OS X v10.6 (Snow Leopard) and the Mac OS X v10.5 SDK
 

2.JDK 版本要求

不同的Android版本編譯也需要對應的JDK環境,這裡列出了各版本之間的對應關係

Android版本 JDK版本(Ubuntu) JDK版本(Mac OS)
Android 2.3.x (Gingerbread) - Android 4.4.x (KitKat)
Android 1.5 (Cupcake) - Android 2.2.x (Froyo)

搭建編譯環境

據上面列出的軟硬體要求,我們可以根據自己要編譯的Android版本以及自己的裝置來選擇合適的系統及JDK,接下來我們就來說說如何搭建編譯環境。

這裡我們主要針對Android 7.0的需要的編譯環境分別對Linux和Mac OS進行配置:

設定Linux系統編譯環境

1.安裝JDK

Android 7.0目前需要openJDK 8的JDK環境

Ubuntu 15.04+

如果你的系統是Ubuntu 15.04及以上版本的話,直接執行如下指令即可直接安裝:

Ubuntu 14.04

如果你使用的是Ubuntu 14.04版本,現在並沒有專門針對14.0.4可用的open jdk8的包,

但是Ubuntu 15.04 OpenJDK 8的包可以在14.0.4上成功地執行,所以我們下載Ubuntu 15.04 OpenJDK 8的安裝包來手動安裝:

      sudo apt-get update

      接著依次對上面下載的三個deb檔案執行如下指令進行安裝:

      sudo dpkg -i 下載的檔案地址

     最後執行 apt-get -f 指令修復安裝依賴的包

      sudo apt-get -f install
  1. 更新系統預設使用的JDK版本 
    如果你的系統安裝了多個版本的JDK,可以通過下面的指令執行切換,會彈出可選的JDK版本,根據你的需要選擇對應的版本就可以了:

2.安裝所需要的工具包

Ubuntu 14.04

我們編譯過程中會用到下面的依賴包,執行如下指令統一安裝:

3.設定原始碼編譯輸出路徑

預設情況下,編譯好的系統原始碼會在原始碼所在目錄的out資料夾下, 
如果你希望調整輸出目錄的路徑,可以執行下面的指令指定輸出目錄:

4.設定USB介面訪問裝置

在linux下,預設情況是不允許普通使用者直接通過USB介面來訪問裝置的.

推薦方法是以根使用者身份在 /etc/udev/rules.d/51-android.rules 路徑建立檔案。

我們可以通過如下指令來實現(注意用你的系統username替換指令中的):

設定Mac OS系統編譯環境

Mac OS的檔案系統預設情況下保留了大小寫實際上卻又不區分大小寫。 
目前的Git指令無法支援這樣的檔案系統,會導致一些莫名其妙的錯誤,所以在Mac OS上編譯Android系統原始碼,我們必須先建立一塊區分大小寫的磁碟映象。

建立一塊區分大小寫的磁碟映象

這裡我們直接通過命令列來建立:

該指令會在系統根目錄下生成一個android.dmg或是android.dmg.sparseimage檔案,一旦掛載,將被作為支援Android開發所需格式的驅動映象分割槽。

如果之後你需要更大的空間,你可以通過下面的指令進行空間調整:

你還可以在 ~/.bash_profile 檔案中,新增幫助函式來掛載跟取消掛載:

之後我們就可以通過執行mountAndroid指令來執行掛載映象,通過umountAndroid指令來取消掛載。

安裝JDK

安裝工具依賴包

1. 安裝xcode命令列工具

$ xcode-select --install

對於老版本的Mac OS系統(10.8或者10.8之前的),我們需要到蘋果開發者站點進行下載安裝. 
如果你還沒有註冊成為蘋果開發者,你需要先註冊一個蘋果賬號來進行下載.

2. 到 macports.org 上下載對應Mac OS版本的macports(類似於Linux下的apt-get,用來幫助你安裝其他應用程式)

注意:確保 /opt/local/bin 在路徑 /usr/bin前,如果沒有,在 ~/.bash_profile 檔案中進行新增

export PATH=/opt/local/bin:$PATH

注意:如果根目錄下沒有 .bash_profile 檔案,那就手動建立一個

3. 通過macports安裝make, git以及GPG

$ POSIXLY_CORRECT=1 sudo port install gmake libsdl git gnupg

如果使用的是Mac OS X v10.4版本的系統,還需要安裝bison:

$ POSIXLY_CORRECT=1 sudo port install bison

注意:如果是編譯Android 4.0.x及以下版本的系統,gmake 3.8.2版本存在一個bug,需要還原到gmake 3.8.1

優化編譯環境(可選)

設定ccache

我們可以自由選擇是否開啟ccache編譯工具。

ccache是一個高速編譯快取工具,它通過將標頭檔案快取記憶體到原始檔之中而改進了構建效能,因而通過減少每一步編譯時新增標頭檔案所需要的時間而提高了C\C++的構建速度。

從編譯的全過程來看,不使用ccache的情況下,編譯過程中會多次解析相同的標頭檔案,浪費了處理器週期,更重要的是浪費了開發者的時間,因為他們要等待這一過程的完成。在一個團隊中,這一影響可能會更為明顯,因為團隊成員可能會反覆編譯解析相同的標頭檔案。

所以,一般對於專門用來編譯系統的伺服器或是大容量的生產環境,這個功能比較有用,它可以加速重新編譯的速度。

注意:如果你只是個人開發者,不是專門的編譯伺服器,不需要進行增量構建的話,那麼使用ccache可能會因為快取記憶體缺失而降低你的構建速度。

開啟ccache

要開啟ccache,在原始碼樹的根路徑下執行下面的指令:

快取的大小一般設定為50G-100G

接著在 .bashrc (或者etc/profile)中新增下面的指令

export USE_CCACHE=1

預設情況下,快取會存在home根目錄的~/.ccache中,但是如果你使用的是NFS或者其他的非本地檔案系統,那麼你同樣需要在.bashrc指定快取目錄地址

在Mac OS的系統中,你需要將linux-x86替換成darwin-x86:

prebuilts/misc/darwin-x86/ccache/ccache -M 50G

當編譯的Android系統是4.0.x或者更老的版本,ccache的快取路徑會有所不同

prebuilt/linux-x86/ccache/ccache -M 50G

這個設定會一直儲存在CCACHE_DIR中。

在Linux上,你可以通過以下指令開啟對ccache的監聽:

$ watch -n1 -d prebuilts/misc/linux-x86/ccache/ccache -s

下載原始碼

編譯環境配置好之後,我們就可以開始下載我們的原始碼了

安裝Repo

Repo是google用Python寫的一個指令碼工具,Android使用git作為程式碼管理工具,一個Android系統由N多個git庫構成,如果手動進行一個個下載,那簡直是一件非常痛苦的事,而repo就是用來對這些git庫進行維護管理跟下載的。

通過Repo工具,我們可以輕鬆地完成Android系統原始碼的下載。

1.在系統home根路徑下建立bin目錄並且新增到path路徑中:

2.下載repo工具並設定其可執行

初始化Repo客戶端

1.建立一個空目錄用來存放我們的Android系統原始碼,名字自己隨便定

2.初始化repo倉庫

從主幹master下載原始碼,目前最新版本

如果需要下載某個特定版本系統的分支,可以在上述命令後加-b 版本分支號,這裡我指定Android 7.0的版本分支

具體的版本分支號可以到這個地址檢視(需要FQ): 
Android系統個版本分支號

3.同步原始碼到本地

這時執行sync指令便可以自動下載原始碼到本地了

使用國內映象下載原始碼

由於國內網路的問題,上述操作的原始碼下載需要FQ才能進行,速度會受到很大影響,幾十G的系統原始碼可能需要花上上週的時間才能下完,

因此我們可以選擇國內的映象進行原始碼下載:

清華大學的映象站 
參照頁面上的描述對上面的指令稍作調整便可以了,站點上寫得比較詳細,這裡我們就不作贅述了。

根據網速的不同,一般一天之內能夠下載完畢。

對於下載下來的原始碼,我們並不能直接刷到我們的目標裝置上或者是使用模擬器執行,我們必須對原始碼進行編譯生成對應的image二進位制映象檔案, 
當然你也可以直接從官網下載對應系統版本的映象檔案:

裡我們還是自己來編譯下原始碼,熟悉下整個編譯過程。

原始碼編譯

首先我們通過命令列進入到原始碼目錄中,我這裡目錄的名稱是aosp

cd aosp

清空輸出目錄

為了確保我們編譯生成的檔案不受之前build構建的檔案影響,我們在原始碼目錄中執行下面的指令,該指令會清空out輸出目錄中的所有檔案

$ make clobber

設定編譯環境

首先我們通過原始碼build目錄中的 envsetup.sh 指令碼檔案初始化我們的編譯環境,執行

$ source build/envsetup.sh

$ . build/envsetup.sh

這兩個指令的效果是一樣的,會初始化一些有用的命令工具

我們後面執行的一些指令必須在初始化 envsetup之後才能執行

選擇編譯目標

接著我們通過 lunch 指令來選擇我們需要編譯的目標 
執行lunch指令

$ lunch

會彈出可選目標項:

所有的構建目標由BUILD-BUILDTYPE的形式組成: 
BUILD對應codename

這是官方提供的一份對照表:

這是官方提供的一份對照表:

Device Code name Build configuration
Pixel XL marlin aosp_marlin-userdebug
Pixel sailfish aosp_sailfish-userdebug
HiKey hikey hikey-userdebug
Nexus 6P angler aosp_angler-userdebug
Nexus 5X bullhead aosp_bullhead-userdebug
Nexus 6 shamu aosp_shamu-userdebug
Nexus Player fugu aosp_fugu-userdebug
Nexus 9 volantis (flounder) aosp_flounder-userdebug
Nexus 5 (GSM/LTE) hammerhead aosp_hammerhead-userdebug
Nexus 7 (Wi-Fi) razor (flo) aosp_flo-userdebug
Nexus 7 (Mobile) razorg (deb) aosp_deb-userdebug
Nexus 10 mantaray (manta) full_manta-userdebug
Nexus 4 occam (mako) full_mako-userdebug
Nexus 7 (Wi-Fi) nakasi (grouper) full_grouper-userdebug
Nexus 7 (Mobile) nakasig (tilapia) full_tilapia-userdebug
Galaxy Nexus (GSM/HSPA+) yakju (maguro) full_maguro-userdebug
Galaxy Nexus (Verizon) mysid (toro) aosp_toro-userdebug
Galaxy Nexus (Experimental) mysidspr (toroplus) aosp_toroplus-userdebug
Motorola Xoom (U.S. Wi-Fi) wingray full_wingray-userdebug
Nexus S soju (crespo) full_crespo-userdebug
Nexus S 4G sojus (crespo4g) full_crespo4g-userdebug
構建型別 用途
user 有限的訪問許可權,主要用於釋出正式產品,沒有root跟除錯許可權
userdebug 跟user型別差不多,但是多了root跟debug除錯許可權
eng 擁有各種除錯工具的開發版設定,擁有root跟debug許可權

如果作為開發使用的話,那我們一般都是選 -eng

這裡我自己是準備在模擬器上執行編譯的image映象,並且我電腦的cpu是intel x86的,所以我選擇了 6. aosp_x86-eng

我們可以根據自己的需要選擇對應的cpu型別。

注意:我們知道,Android官方的模擬器速度很慢, 
Intel特意提供了一個叫HAXM的虛擬硬體加速技術,全稱為:Intel Hardware Accelerated Execution Manager.

只要你的CPU是intel的產品並且支援VT(virtualization Technology)就可以使用HAXM技術將你的模擬器的速度提升至真機的水平。

目前Intel只提供了windows版和MAC版,Linux系統只有通過安裝KVM來達到這個效果。

安裝KVM

首先我們檢測下自己的cpu是否支援 hardware virtualization(硬體虛擬化)

egrep -c '(vmx|svm)' /proc/cpuinfo

輸出的值如果是大於0的,則表明你的cpu支援

如果你使用的是vmware虛擬機器安裝的linux,注意要設定下虛擬機器的cpu來支援硬體虛擬化,先關閉虛擬機器,然後右鍵虛擬機器=》設定,選中cpu,勾選虛擬化Intel VT項,這樣就能支援KVM了。

對於Ubuntu 10.0.4以上的版本,我們通過下面的指令安裝KVM即可

sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils

這樣,在編譯完目標intel cpu的映象檔案後,我們執行模擬器就會自動進行加速了。

編譯原始碼

好了,一切就緒,我們可以開始編譯我們的原始碼了, 
我們在原始碼路徑下通過 make -jN 指令來進行原始碼編譯,這裡的N一般建議設定為cpu核心執行緒數的1-2倍。

$ make -j4

一般情況下,我們等待原始碼編譯完成就可以了,不過從Android 7.0 N開始,make指令預設會開啟Jack編譯工具鏈來進行Java程式碼的編譯,這個過程中可能會出現一些問題。

什麼是Jack編譯工具鏈(The Jack toolchain)?

我們知道,我們平時編譯Android程式碼的時候會先將Java程式碼編譯成.class檔案,最終再轉換成.dex檔案,如圖:

而Jack編譯工具鏈則跳過了編譯成.class檔案這一過程,直接將Java程式碼編譯成.dex檔案

它的優勢:

  • 完全開放原始碼 
    原始碼均在AOSP中,合作伙伴可貢獻原始碼

  • 加快編譯原始碼 
    Jack 提供特殊的配置,減少編譯時間:pre-dexing, 增量編譯和Jack編譯伺服器.

  • 支援程式碼壓縮,混淆,重打包和multidex

  • 不在使用額外單獨的包,例如ProGuard。

如果想進一步瞭解Jack,可以訪問Compiling with Jack(需FQ),這裡就不作太多解釋了。

按照官方的說法Jack可以加快編譯速度

但實際編譯過程中,在我的裝置上Jack會佔用大量記憶體,並且拖慢編譯速度,還會報錯,而且官方文件上寫的配置方式對Jack並不起作用。

Jack編譯過程中遇到的問題

編譯過程中報Out of memory error並中斷編譯

在執行make指令後,當第一次編譯Java程式碼的時候,Jack會被啟用,這個時候經常會卡住,並且一段時間後報錯Out of memory error

按照官方的說法,Jack並行編譯的時候佔用的資源太大導致記憶體溢位了

可以通過在 $HOME/.jack 檔案中減小 SERVER_NB_COMPILE 的值來減小並行編譯數量, SERVER_NB_COMPILE 的值預設為4

但實際情況是Jack沒有在根路徑下生成 .jack 檔案,並且手動建立設定 SERVER_NB_COMPILE 後重啟Jack服務也沒有效果。

經測試發現make編譯過程中當Jack第一次被啟用時會在home根路徑下生成.jack-server目錄

可以通過修改該目錄中config.properties檔案裡的 
.jack.server.max-service
值來設定併發執行緒數。

同時,你也可以設定增加Jack的記憶體容量來解決這個問題,指令如下

export JACK_SERVER_VM_ARGUMENTS="-Dfile.encoding=UTF-8 -XX:+TieredCompilation -Xmx4096m"

然後進入到輸出路徑的bin目錄下:

cd /home/cjpx00008/aosp/out/host/linux-x86/bin

執行下面的指令重啟Jack服務

./jack-admin stop-server./jack-admin start-server

我改了service大小,同時增加了記憶體,後來編譯的過程中沒有發生其他問題。

有辦法關閉Jack編譯嗎?

既然Jack有問題,那我們可以關閉Jack編譯嗎?

目前來說我還沒有找到如何在Android 7.0編譯的時候關閉Jack,如果有知道的小夥伴歡迎留言告訴我哈,感激不盡!!

執行編譯出的image映象

經過漫長的等待,我們的原始碼終於編譯結束了,是時候來執行編譯出的image映象了。

這時我們只要在原始碼目錄下執行 emulator 指令即可執行模擬器

$ emulator

第一次啟動時間可能會有點長,耐心等待即可 

執行成功了,是不是有點小激動呢!

注意:如果你的命令列視窗關閉重開了,那emulator指令可能會提示找不到命令,我們可以在原始碼根目錄環境下,通過envsetup.sh重新初始化命令,執行lunch指令選擇編譯目標,這個時候你再執行emulator就不會提示找不到指令了(每次關閉命令列視窗都需要重新執行如下指令才能執行emulator)

也可以通過配置環境變數來設定emulator指令,不過我沒有成功,哈哈

$ source build/envsetup.sh$ lunch$ emulator

好啦,到此,我們的原始碼就編譯完畢啦,下一篇我們來聊聊如何使用Android Studio匯入Android系統原始碼,並通過AS進行Java原始碼除錯,以及使用GDB來除錯系統Native C\C++原始碼。

到了這裡,就是一長篇文章告一段落了,之後對Java程式碼除錯,通過命令列實現。  (*打個星號)

開始...

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

    1. Java 相關程式碼的除錯

    2. 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

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

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 檔案根據自己需要調整要載入的原始碼。

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

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

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

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

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

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

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

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

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

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

開始除錯原始碼

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

確保 Android Studio 允許 ADB 除錯

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

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

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

點選 WebViewBrowser 開啟 app

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

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

如圖所示我們可以看到,程式碼成功進入了斷點,然後我們就可以隨心所欲地除錯我們想要的除錯的 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:

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

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

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

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

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

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

    # gdbclient <app pid> 可以啟用 gdb 除錯對應 PID 程序
    
    gdbclient 2157

    等待進入 gdb 除錯命令介面

  5. 使用 GDB b 命令打斷點

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

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

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

    我們輸入設定斷點命令:

    b frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp:71

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

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

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

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

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

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++ 程式碼的話,也可以參考下面的兩篇文章,不過個人覺得這種方法有一定的侷限性。

 兩篇文章先到這裡結束,技術文章很多,但是真正使用起來又可以總結出新的結論和技術操作上的更新。