HaaS100開發除錯系列 之 使用AliOS Things診斷除錯元件定位Bug
技術標籤:AliOS Things物聯網作業系統嵌入式實時作業系統debugger物聯網HaaS
1、背景
在嵌入式開發中,我們經常遇到的一個問題是:寫程式碼一個不小心,就製造了一個bug,C語言中bug的威力大家也心知肚明——可以直接把系統搞掛!
即大家常見的系統宕機、系統重啟等等;而問題的來源或者根因,又常常使得我們束手無策,只好採用“列印”大法,一遍遍的加printf。
而每次改程式碼又要經歷痛苦的“編譯-燒寫-執行-復現”這個過程,不知不覺,一天過去了,bug還沒解。
所以我們經常想,要是系統能直接告訴我們bug在哪、是什麼錯誤導致的就好了,直接改程式碼分分鐘搞定,可以節省很多開發時間!
這也是我們常用一些模擬器(如JLINK)的原因,系統掛死後可以掛上模擬器,檢視PC在哪,通過bt檢視呼叫棧等來幫助我們定位。
而這又對硬體有了一定的要求——要能支援模擬器連線,有時還要開發者折騰一下環境。
我們的HaaS100雖然也支援硬體連線模擬器(參考上一篇帖子:HaaS100開發除錯系列 之 如何使用J-Link模擬器除錯程式碼)。
這裡我們告知大家一個更方便定位系統異常宕機的方法——AliOS Things的診斷除錯元件。
2、診斷除錯元件簡介
診斷除錯包含的內容很多,前面我們介紹過一些除錯命令,參見文章《一文輕鬆入門HaaS100診斷除錯系統》。
本文我們重點介紹的是AliOS Things的診斷除錯元件是怎麼幫助解決程式碼bug
診斷除錯元件可以縮短bug定位時間。
如果一個bug出現導致系統異常後,使用者可以不用連模擬器、不用加列印、不用開啟gdb單步除錯的情況下,可以快速找到bug原因。
或者幫助使用者指出可能的異常點,進而修復以節省開發時間。
舉例說明:
- 程式碼中訪問了非法記憶體(比如:在不可寫的地址處寫了資料,如訪問了0地址)導致系統奔潰,AliOS Things診斷除錯元件可以記錄訪問非法記憶體時的pc值,告訴使用者掛在了哪一行;
- 程式碼跑飛了(pc=0),AliOS Things診斷除錯元件記錄了函式呼叫的棧,並根據棧向上回溯可以找到A->B->C的函式呼叫過程,與模擬器中bt命令類似;
- 使用者記憶體申請時malloc 失敗,AliOS Things診斷除錯元件可以記錄使用者此時申請了多少記憶體、此時系統還有多少記憶體可以供申請、使用者是在哪個任務中申請的記憶體、從系統啟動開始記憶體的申請情況等資訊,這些資訊可以幫助開發者定位是否有記憶體洩漏的情況。
- ......
AliOS Things的診斷除錯元件可以幹很多事,後面我們會陸續推出文章來介紹。
今天我們只看一個問題——bug產生了,系統異常掛死了,那麼AliOS Things會做哪些事呢?
簡單一句話回答,輸出重要的log幫助大家定位問題,這也是AliOS Things的診斷除錯元件最重要的部分。
3、AliOS Things的異常log到底是啥樣
直接上HaaS100輸出的log
!!!!!!!!!! Exception !!!!!!!!!!
========== Regs info ========== 異常現場暫存器資訊
R0 0x00000000
R1 0x34027F20
R2 0x34027F30
R3 0x340251B4
R4 0xFFFFFFFF
R5 0x00000000
R6 0x2C0D2C72
R7 0x00000001
R8 0x2C0D2C86
R9 0x2C0D236B
R10 0x00000000
R11 0x00000000
R12 0x0000C000
LR 0x1C5D6CC3
PC 0x1C5D6CC2
xPSR 0x61000000
SP 0x34025118
EXC_RET 0xFFFFFFBC
EXC_NUM 0x00000006
PRIMASK 0x00000000
FLTMASK 0x00000000
BASEPRI 0x00000000
CFSR 0x01000000
HFSR 0x00000000
MMFAR 0xE000ED34
BFAR 0xE000ED38
AFSR 0x00000000
========== Stack info ========== 異常現場棧資訊
stack(0x34025118): 0x34027D20 0x340251B4 0x00000000 0x34022F98
stack(0x34025128): 0x00000000 0x34682380 0x1C5D6BBD 0x00000005
stack(0x34025138): 0x00000006 0x1C5D621F 0x00000003 0x2C0D2A0C
stack(0x34025148): 0x340230C4 0x00000013 0x34682280 0x00000000
stack(0x34025158): 0x000000F7 0x00000005 0x00000003 0x00000000
stack(0x34025168): 0x00000000 0x00000000 0x00000001 0x34682380
stack(0x34025178): 0x34022F98 0x0000000B 0x34022FA8 0x00000000
stack(0x34025188): 0x000000F6 0x00000001 0x2C0D294D 0x1C5D63D3
stack(0x34025198): 0x00000000 0x2C0D1BD0 0x00000000 0x0D000000
stack(0x340251A8): 0x78300070 0x66666666 0x66666666 0x00003100
stack(0x340251B8): 0x00000000 0x00000000 0x00000000 0x00000000
stack(0x340251C8): 0x00000000 0x00000000 0x00000000 0x00000000
stack(0x340251D8): 0x00000000 0x00000000 0x00000000 0x00000000
stack(0x340251E8): 0x00000000 0x00000000 0x00000000 0x00000000
stack(0x340251F8): 0x00000000 0x00000000 0x00000000 0x00000000
stack(0x34025208): 0x00000000 0x00000000 0x00000000 0x00000000
========== Call stack ========== 棧回溯資訊,可以得出函式呼叫過程
backtrace : 0x1C5D6CC2
backtrace : 0x1C5D621C
backtrace : 0x1C5D63CE
backtrace : ^task entry^
========== Heap Info ========== 系統此時的記憶體資訊,可以看出記憶體申請了多少,還剩多少
---------------------------------------------------------------------------
[HEAP]| TotalSz | FreeSz | UsedSz | MinFreeSz | MaxFreeBlkSz |
| 0x00680000 | 0x0065A300 | 0x00025D00 | 0x00659E20 | 0x0065A300 |
---------------------------------------------------------------------------
========== Task Info ========== 系統當前任務狀態資訊,可以看出任務棧是否過小
--------------------------------------------------------------------------
TaskName State Prio Stack StackSize (MinFree)
--------------------------------------------------------------------------
dyn_mem_proc_task PEND 0x00000006 0x2004B938 0x00000400(0x0000035C)
idle_task RDY 0x0000003D 0x2004BE0C 0x00001000(0x00000F94)
DEFAULT-WORKQUEUE PEND 0x00000014 0x2004F1E8 0x00000C00(0x00000B7C)
timer_task PEND 0x00000005 0x2004D0D8 0x00002000(0x00001F48)
main SLP 0x00000021 0x2015A000 0x00005000(0x000044C4)
transq_msg PEND 0x0000001F 0x3469A4C4 0x00001000(0x00000680)
apps_recover SLP 0x00000021 0x2004A588 0x00001000(0x00000F64)
temp_main SLP 0x00000021 0x346A15D8 0x00001000(0x00000F80)
main_task SLP 0x00000020 0x34002668 0x00020000(0x0001F6C4)
cli RDY 0x0000003C 0x340232D0 0x00002000(0x0000180C)
ulog PEND 0x0000003C 0x34026890 0x00000C00(0x00000A58)
========== Queue Info ========== AliOS Things kernel queue使用資訊
-------------------------------------------------------
QueAddr TotalSize PeakNum CurrNum TaskWaiting
-------------------------------------------------------
======== Buf Queue Info ======== AliOS Things kernel buf queue使用資訊
------------------------------------------------------------------
BufQueAddr TotalSize PeakNum CurrNum MinFreeSz TaskWaiting
------------------------------------------------------------------
0x2004FDE8 0x000001E0 0x00000000 0x00000000 0x000001E0 timer_task
0x34025420 0x00001400 0x00000000 0x00000000 0x00001400 ulog
=========== Sem Info =========== AliOS Things kernel semphore使用資訊
--------------------------------------------
SemAddr Count PeakCount TaskWaiting
--------------------------------------------
0x2004CF60 0x00000000 0x00000000 dyn_mem_proc_task
0x2004F1B8 0x00000000 0x00000000 DEFAULT-WORKQUEUE
0x340023A0 0x00000001 0x00000001
0x34002478 0x00000000 0x00000000
0x340025D0 0x00000001 0x00000001
0x34682C34 0x00000000 0x00000000
0x34682C58 0x00000000 0x00000000
0x340275B0 0x00000000 0x00000000
!!!!!!!!!! dump end !!!!!!!!!!
3.1、Log分析
上面的log是在HaaS100上產生系統異常後,由AliOS Things輸出的log。log可以分為:
- 異常現場暫存器:跟arch相關的通用暫存器和一些特殊暫存器資訊;
- 異常棧資訊:產生異常的任務的棧資訊;
- 棧回溯資訊:產生異常的呼叫棧,類似模擬器中的bt命令,這個是異常log中最重要的部分;
- 記憶體資訊:系統此時的記憶體狀態,對於定位一些記憶體洩漏問題比較有用;
- 任務資訊:系統當前的任務狀態資訊,對於定位任務棧溢位問題比較有用;
- 核心資訊:包含了kernel 中的queue、buf_queue 和 sem狀態。
log中所示的記憶體包含了很多核心相關的內容,後續我們也會推出文章來介紹AliOS Things的核心。
3.2、如何開啟診斷除錯元件
使用者只需要在aos.mk裡包含debug元件,重新編譯燒錄上電即可。
$(NAME)_COMPONENTS += debug
3.3、如何產生一個系統異常
理論上任何一個系統異常後,都會出現類似上面的log,如果開發者對產生系統異常感興趣,可以使用下面的簡單方法:
m 0xffffffff 1
即使用系統提供的cli 命令,改寫系統位於0xfffffff出的記憶體值為1,地址0xfffffff在HaaS100上為不可寫的區域,改寫這個值可以觸發系統異常,打印出上面的log。
使用cli命令的方法可以參考另外一篇文章《一文輕鬆入門HaaS100診斷除錯系統》
3.4、呼叫棧的價值
呼叫棧的資訊輸出是AliOS Things診斷除錯元件的核心,我們通過上面的命令產生異常後,使用toolchain自帶的arm-none-eabi-addr2line 命令對上面log中的call stack呼叫棧中的地址進行解析,使用方法是:
arm-none-eabi-addr2line -pfiCe xxx.elf addr
以log中輸出的call stack地址為例:
./build/compiler/gcc-arm-none-eabi/Linux64/bin/arm-none-eabi-addr2line -pfiCe out/[email protected]/binary/[email protected] 0x1C5D6CC2 0x1C5D621C 0x1C5D63CE
可以解析出呼叫棧所對應的程式碼位置,如:
pmem_cmd at /workspace/hass/AliOS-Things/core/cli/cli_default_command.c:224
proc_onecmd at /workspace/hass/AliOS-Things/core/cli/cli.c:173
(inlined by) cli_handle_input at /workspace/hass/AliOS-Things/core/cli/cli.c:290
cli_main at /workspace/hass/AliOS-Things/core/cli/cli.c:781
我們可以清楚看到發生異常的函式呼叫過程,並且指出了函式程式碼的路徑和行號。
cli_main -- > proc_onecmd ---> pmem_cmd
4、筆者的話
大家有沒有覺得,通過這個方法定位Bug,讓異常發生的位置一目瞭然,我們快速找到這行程式碼後修改,分分鐘解決了這個Bug。又可以開心的繼續幹活了!
不過,AliOS Things診斷除錯元件只是儘可能的幫助大家節省解Bug的時間,而有些Bug的產生並不會導致系統異常,但會給系統埋下不穩定的伏筆,這個時候再好的診斷工具也沒用了。
大家還是要多修煉寫程式碼內功,不產生bug才是我們的追求!
5、開發者技術支援
如需更多技術支援,可加入釘釘開發者群
更多技術與解決方案介紹,請訪問阿里雲AIoT首頁https://iot.aliyun.com/