HaaS100開發調試系列 之 使用AliOS Things診斷調試組件定位Bug

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/debug_demo@haas100/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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章