背景
在嵌入式开发中,我们经常遇到的一个问题是:写代码一个不小心,就制造了一个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会做哪些事呢?
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可以分为:
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/debug_demo@haas100.elf 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、开发者技术支持
如需更多技术支持,可加入钉钉开发者群,或者关注微信公众号
建站 什么 虚拟主机 够用?这要看搭建的是什么类型的网站。比如个人博客类型的网...
信息化2.0时代提出开展智慧教育创新发展行动。2019年2月,中共中央、国务院印发...
前提条件 请您在购买前确保已完成注册和充值。详细操作请参见 如何注册公有云管...
Docker生成新镜像版本的两种方式 There are two ways Docker can generate new m...
在Python语言中有如下3种方法: 成员方法 类方法(classmethod) 静态方法(staticm...
【51CTO.com快译】 数据可视化工具不断发展,提供更强大的功能,同时改善可访问...
2021年3月24日,主题为《数据的世界,世界的数据》的星环科技2021春季新品发布会...
从 10.0.0 版开始,异步迭代器就出现在 Node 中了,在本文中,我们将讨论异步迭...
本文整理自直播《Hologres 数据导入/导出实践-王华峰(继儒)》 视频链接: https:/...
摘要 元旦期间 订单业务线 告知 推送系统 无法正常收发消息,作为推送系统维护者...