TShopping

 找回密碼
 註冊
搜索
查看: 269|回復: 0

[教學] Android NDK斷點失效原因及解決方案

[複製鏈接]
發表於 2014-10-22 15:30:21 | 顯示全部樓層 |閱讀模式
 
Push to Facebook Push to Plurk Push to Twitter 

這篇文章主要列舉了解決幾種斷點失效的經驗和方法,對於那些苦苦掙扎在莫名其妙的問題中的開發者也許有較大的幫助。

概述

首先,讓我們看一下一個典型的包括本地代碼的Andr​​oid應用的結構:

1.png

應用代碼被打包存放在一個.apk文件中,實際上,.apk文件就是一個ZIP格式壓縮包,壓縮包中包括一個classes.dex文件(所有的Java代碼都包含其中)和一個或更多存放在lib\<EABI名稱>目錄下的本地代碼庫文件。正常情況下,包含本地代碼的應用的工作步驟如下所述:
1. Android系統加載Java代碼,開始執行
2. Java代碼中通過System.loadLibrary()方法加載本地代碼庫
3. Java代碼調用本地代碼實現的函數

每個本地代碼庫都有兩個版本:

  • 一個帶有調試信息的完整版本,此版本的庫文件包含對應的源代碼,會將庫文件中的二進制代碼地址和源代碼文件的行相對應。這個版本的文件存放在你的項目路徑下的obj\local\armeabi目錄中。
  • 一個不帶調試信息的版本,這個文件位於libs\armeabi目錄下,最後真正打包到apk文件中的庫文件是這個版本的文件。
2.png
設置斷點

當你在源代碼文件中的某一行設置斷點時,GDB需要先進行一些操作才能真正的創建該斷點並且讓這個斷點可以背觸發。
假設說libNative1已經被加載了,內存中起始地址為0×10000,然後用戶想要在源代碼文件c:\main.cpp的第24行設置了一個斷點,GDB將會做如下操作來設置這個斷點:

  • GDB搜索搜有的已加載庫文件中搜索”c:\main.cpp”文件對應的庫文件,在本文的例子中,就是libNative1.so庫文件了。
  • GDB在obj\local\armeabi目錄下超找一個叫做libNative1.so的文件,如上所述,這個目錄下的文件是帶有調試信息的。GDB在這個庫文件中的源代碼文件(c:\main.cpp)中讀取符號表。
  • GDB根據讀取出來的符號表計算第24行對應的地址偏移量是+0×2。然後將這個偏移量加上庫文件libNative1.so的起始地址(0×10000),並且在0×10002的位置設置斷點。

這3步操作任意一步失敗,斷點就不會被創建,或者創建錯誤從而導致永遠都不會被觸發。我們在下一小節介紹這種問題的診斷方法以及如何修復它。

診斷問題

本小節將詳細描述如何檢查由於不規範配置或文件缺失導致斷點失效的方法。

A. 確保庫文件已被加載

第一件要做的事情是確定本地代碼庫是不是已經被加載了,並且GDB是不是已經知道這件事情。我們可以在GDB中輸入“info shared”命令來查看。

3.png

info shared命令結果包含三個重要信息:
1. linker連接器
2. libc.so文件
3.你想要調試的所有本地代碼庫文件
這提供一個例子:

From To Syms Read Shared Object Library
……………………………………………………No com.visualgdb.example.MyAndroidApp
0xb0001000 0xb00069d0 Yes C:/…/AndroidBinaryCache/…/linker
0× 40073300 0x400a12fc Yes C:/…/AndroidBinaryCache/…/libc.so
0x5148fccc 0×51491198 Yes E:/…/obj/local/armeabi/libMyAndroidApp.so

如果有些庫文件沒在這顯示出來,那麼可以通過”sharedlibrary”手動強制GDB重新加載一次符號表。
如果你有些.so文件(例如libMyAndroidApp.so)已經在這裡列出來了,但是顯示符號沒被加載(“Sysms read”那一列的狀態是“no”),這說明GDB沒法找到帶有調試信息的庫文件。這時可以在GDB中使用“show solib-search-path”命令:

4.png

可以看到你項目的obj/local/armeabi目錄路徑被打印出來,該目錄下應該包含了帶有調試信息的.so文件,如果在該目錄下沒有.so文件的,那麼將正確的.so文件複製到該目錄下並重新運行”sharedlibrary”命令。
除此之外,你還可以使用”set solib-search-path”命令強制GDB在其他目錄下搜索.so文件。
如果你的.so文件還是沒有列出來,說明還沒有被Java代碼加載,請再確認一下Java代碼中的System.loadLibrary()方法是不是被正確調用,並且沒有產生異常。

B. 確保使用正確的文件路徑

還有一個比較常見的導致斷點失效的原因是你再生成和調試項目時使用不同的目錄。舉個例子,如果你編譯庫文件時項目源代碼位於c:\projects,然後你又將源代碼移到d:\projects目錄下,這時在d:\projects\main.cpp文件中設置一個斷點就會失敗,GDB並不會使用d: \projects\main.cpp這個源文件替代原來目錄下的文件。像這一類問題都可以通過檢查源文件並且和設置斷點時的源文件做比較來解決,你可以先移除所有斷點,然後重新設置這些斷點,再看看你的IDE中顯示的GDB命令,看看到底是在哪個源文件中設置的斷點,是不是和生成庫文件時的源文件一致。

5.png

從上圖可以看到用來設置斷點的-break-insert命令可以顯示出你使用的源文件路徑,再使用”info sources”命令可以看看GDB真正發現的是哪些源文件。

6.png

將這條命令的輸出結果復製到一個文本編輯器中,然後再去查找你想調試的源文件(比如說說MyAndroidApp.c):

7.png

如果在輸出結果裡沒有你想要調試的源文件,那麼說明你家在的.so文件版本錯了(關於.so文件的版本問題見上一小節)。如果數據結果有你想要調試的文件,但是路徑和你使用-break-insert命令時顯示的源文件路徑不一致,這是你就需要再找找什麼原因了。這種情況下,在新路徑下重新編譯生成你的項目,或者將源文件放回到老的路徑下,這樣就可以保證你設置斷點的源文件和”info sources”命令輸出的源文件路徑保持一致了。
再次強調,GDB需要你設置斷點的源文件路徑和info sources命令打印出來的文件路徑完全一致。包括jni前面的雙斜線。

C.重新檢查文件版本並且做一次清理操作

如果你設置了斷點,但是從來沒有生效,也有一種可能是你的Andr​​oid應用加載的.so文件和你的GDB用來計算地址的.so文件版本不一致造成的。導致這種情況的最常見原因是Android加載時有bug,它沒有去加載armeabi-v7a目錄下的文件,而是加載了armeabi目錄下的庫文件。如果你覺得有可能是這個問題,試試修改編譯生成的配置,要么在armeabi平台編譯,要么在armeabi-v7a平台編譯,千萬不要同時再者兩個平台下編譯。(【譯者註】原作者前面沒有提到armeabi-v7a,現在突然冒出來了,armeabi和armeabi-v7a是不同的CPU類型,後者帶有包括浮點運算等其他功能,通過Android.mk文件中的target_cpu_api來指定,如果選擇了armeabi-v7a的話,前面提到的xxx\armeabi目錄都會變成xxx\armeabi-v7a,一個帶有NDK開發的Andr​​oid項目中可能有多個Android.mk,原作者的意思就是所有的Andr​​oid.mk文件的target_cpu_api設置都要一樣)



 

臉書網友討論
您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2016-12-7 04:14 , Processed in 0.061330 second(s), 25 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表