前言¶
概述
本文档主要用于BS2X平台日志分析指导,包括平台日志分类、重启场景、panic类型等。为开发人员、验证人员、使用人员提供参考,提高使用者问题定位效率。
读者对象
本文档主要适用以下工程师:
技术支持工程师
软件开发工程师
符号约定
在本文中可能出现下列标志,它们所代表的含义如下。
符号 |
说明 |
|---|---|
|
表示如不避免则将会导致死亡或严重伤害的具有高等级风险的危害。 |
|
表示如不避免则可能导致死亡或严重伤害的具有中等级风险的危害。 |
|
表示如不避免则可能导致轻微或中度伤害的具有低等级风险的危害。 |
|
用于传递设备或环境安全警示信息。如不避免则可能会导致设备损坏、数据丢失、设备性能降低或其它不可预知的结果。 “须知”不涉及人身伤害。 |
|
对正文中重点信息的补充说明。 “说明”不是安全警示信息,不涉及人身、设备及环境伤害信息。 |
修改记录
文档版本 |
发布日期 |
修改说明 |
|---|---|---|
03 |
2025-06-20 |
|
02 |
2025-05-30 |
|
01 |
2024-07-05 |
第一次正式版本发布。 |
概述¶
文档针对PLT的各类业务,从日志类型、异常场景等方面给PLT维测指导。
日志说明¶
平台日志接口¶
BS2X SDK中提供了普通串口打印和HSO(协议日志)串口打印两种串口LOG打印方式,帮助用户添加、分析维测信息。
普通串口打印¶
普通打印方式通过串口工具直接解析,默认使用UART_BUS0【软件配置参数所选UART句柄名称】(UART_L0【硬件PIN脚名称】)打印串口日志以及发送AT命令,UART BUS具体对照如下表1所示。使用普通打印接口需要包含soc_osal.h头文件,调用osal_printk接口格式化输出LOG。
表 1 UART默认对应表
软件配置参数UART句柄名称 |
硬件PIN脚名称 |
|---|---|
UART_BUS0 |
UART_L0 |
UART_BUS1 |
UART_H0 |
UART_BUS2 |
UART_L1 |
DIAG串口打印¶
DIAG打印方式通过DebugKits工具解析日志,默认使用UART_BUS1【软件配置参数所选UART句柄名称】(UART_H0【硬件PIN脚名称】)打印DIAG日志。HSO维测功能模块提供以下功能:
日志打印。用户可以通过日志打印接口将调试信息打印到DebugKits的Message界面。
命令的注册和处理。用户可以通过注册命令和命令处理函数,来实现在DebugKits工具的命令行界面输入命令来控制单板侧的操作。
系统维测信息获取。DIAG支持获取系统统计类信息(如内存使用、任务信息),帮助用户定位问题。
以用户在chips/bs2x/main_init/app_os_init.c中增加调试日志为例,流程如下:
在chips/bs2x/main_init/app_os_init.c中调用日志打印接口,输出调试信息。需要包含log_def.h、log_common.h头文件。
函数
说明
oml_pf_log_print0(mid, lognum, lvl, fmt)
mid:本条打印归属的模块,调试使用LOG_PFMODULE模块。
lognum:打印的log编号,调试使用LOG_NUM_DEBUG。
lvl:打印等级,定义在middleware/utils/common_headers/log_types.h中。
fmt:打印字符串。
oml_pf_log_print1(mid, lognum, lvl, fmt, p1)
前四个参数含义同oml_pf_log_print0接口描述一致,p1表示打印字符串里携带的1个参数
oml_pf_log_print2(mid, lognum, lvl, fmt, p1, p2)
前四个参数含义同oml_pf_log_print0接口描述一致,p1,p2表示打印字符串里携带的2个参数
oml_pf_log_print3(mid, lognum, lvl, fmt, p1, p2, p3)
前四个参数含义同oml_pf_log_print0接口描述一致,p1,p2,p3表示打印字符串里携带的3个参数
oml_pf_log_print4(mid, lognum, lvl, fmt, p1, p2, p3, p4)
前四个参数含义同oml_pf_log_print0接口描述一致,p1,p2,p3,p4表示打印字符串里携带的4个参数
用户在使用DIAG日志之前,需要先确认调用接口的文件是否被添加到模块当中。例如在chips/bs2x/main_init/app_os_init.c文件中使用LOG_PFMODULE模块打印接口,需要在chips/bs2x/dfx/include/log_def_pf.h添加文件ID,文件ID的格式为:调用日志接口的文件的文件名大写并加_C后缀,如:MAIN_C。
编译程序,编译过程中将生成output/bs2x/database_evb目录。
在DebugKits中执行update HDB,将“步骤3”生成的数据库更新至DebugKits的数据库中。打开DebugKits的Message界面查看日志信息(请参见《BS2XV100 DebugKits工具 使用指南》)。
说明: 请注意,DIAG日志只能支持长度32位及以下的参数,如:"%d"、"%u"、"%x"、"%p",无法支持长度大于32位的参数,如"%ld"、"%s",无法支持浮点参数,如"%f"。 普通打印方式直接在接口调用的位置通过串口进行输出,如果日志过多会阻塞当前调用者线程,可能会出现非预期现象;而DIAG打印方式是通过事件方式抛消息给log线程进行打印,不会阻塞打印调用者,但是会导致codesize增大,内存足够的情况下建议使用,相对来说会比普通方式更灵活。请根据实际场景选择使用。
平台日志分类¶
SDK启动日志¶
如图1所示,开机启动,Flashboot初始化后,串口会打印系统启动原因,系统启动次数及系统异常启动次数。APP启动后,会打印日志初始化、AT初始化以及SDK版本号。如图2所示,HSO同步打印系统上电表示及重启原因,重启次数以及异常重启次数。
图 1 平台启动日志(串口)

图 2 平台启动日志(HSO)

APP睡眠日志¶
App默认每隔一段时间会通过HSO日志记录一次调度空闲时间,打印睡眠统计的同时会喂狗(若出现长时间没有打印,可能会狗超时,因此会喂狗),如图1所示。该维测依赖于PM_MCPU_MIPS_STATISTICS_ENABLE宏,默认关闭,根据实际情况选择,用户可不关心。
图 1 APP睡眠日志

APP panic日志¶
Kernel panic是指操作系统在监测到内部的致命错误,并无法安全处理此错误时采取的动作。内核触发到某种异常情况,运行kernel_panic函数,并尽可能把异常发生时获取的全部信息打印出来。
原理
导致异常的原因多种多样,通过异常打印的调用信息,找到调用kernel_panic的原因。常见的原因包括内核堆栈溢出、内核空间的除0异常、内存访问越界、内核陷入死锁等。
触发方法
内核态读0地址。
APP核发生panic异常时会打印panic原因0x2003以及对应的panic id,如图APP panic日志所示。panic id见表1所示。
图 1 APP panic异常

APP异常重启¶
APP核发生xip异常,hardfault,狗超时等异常时,会打印重启原因0x200x以及pc和lr值,如图1及表1所示。
图 1 APP异常重启

表 1 APP重启原因
异常类型 |
日志类型 |
重启原因 |
|---|---|---|
主动重启 |
Log_FLATFORM |
0x2001 |
panic |
Log_FLATFORM |
0x2003 |
hardfault |
Log_FLATFORM |
0x2004 |
nmi |
Log_FLATFORM |
0x2005 |
狗超时 |
Log_FLATFORM |
0x2008 |
xip异常 |
Log_FLATFORM |
0x2009 |
xip cache异常 |
Log_FLATFORM |
0x200a |
mdma异常 |
Log_FLATFORM |
0x200b |
smdma异常 |
Log_FLATFORM |
0x200c |
强制狗复位 |
Log_FLATFORM |
0x200d |
lp狗/uvlo重启 |
Log_FLATFORM |
0x200f |
异常类型描述¶
表 1 重启描述分析(middleware/utils/dfx/dfx_reboot/reboot/shared/non_os_reboot.h)
id |
描述 |
重启异常场景原因分析 |
|---|---|---|
0x0 |
未知重启原因 |
用户代码中直接写寄存器重启导致 |
0x2001 |
应用核主动重启 |
- |
0x2003 |
APP panic异常 |
需要查看“PANIC ID”中panic id,进一步分析 |
0x2004 |
Hardfault异常 |
非法地址; 空指针; XXX模块未初始化; XXX模块没有时钟; 需要结合临终遗言中mepc,ra,mtval及通用寄存器进一步定位。 |
0x2008 |
APP狗超时 |
结合cpu trace及日志分析。 一般是一直在执行高优先级任务/中断流程,导致低优先级喂狗任务无法得到调度。 |
0x200D |
APP硬狗超时 |
系统卡死后,无法响应狗超时中断,需要结合cpu trace分析 |
0x200f |
ulp狗超时/uvlo重启 |
添加ulp喂狗日志进一步分析 |
0x2010 |
Pin reset |
- |
0x2040 |
升级完成 |
- |
0x2042 |
升级失败 |
- |
0xf0f0 |
系统上电 |
- |
日志分析¶
常见的异常场景包括:watchdog超时,hardfault,mem fault,bus fault,use fault, panic异常,强制狗复位等。发生异常时,会打印异常重启原因,并引发系统重启。其中APP的重启异常用0x2xxx表示,具体有0x2000~0x2010、0x2040~0x2044、0x2080、0x2100。
死机信息获取¶
普通打印口维测信息获取¶
当硬件与串口工具处于连接状态时,如果单板发生死机,串口工具能够接收到复位前DFX流程打印的死机信息。用户可以通过分析死机信息,定位死机原因。普通打印口死机LOG分为4个部分:
死机现场:记录死机发生时RISCV通用寄存器以及部分CSR寄存器的现场值。各个寄存器的描述请参考附录中异常信息查看。

线程栈信息:记录死机发生时正在运行的线程栈的栈顶、栈大小以及栈使用峰值。

栈回溯:回溯死机栈信息,traceback 0为栈顶位置,sp addr,current sp为当前栈位置,sp content为栈内容。

CPU Trace:记录CPU过去一段时间的执行的PC与LR。step time存储记录时间,值越大,记录的时间越接近死机现场。
说明:
注意:此处是数采功能,记录的PC值和真实的执行轨迹可能有所差异,并不一样能完全对应!
Trace路径的分析有两种方式:
方式一:用户可以根据编译时生成的application.lst文件查找LR和PC地址对应的函数,定位死机执行现场。
方式二:用户使用IDE提供的CFBB Tools解析Trace,打开工具,选择用户使用的target。

导入CPU Trace Information到CPU Trace end的Trace信息,点击解析可以获取到过去一段时间的执行信息,定位死机原因:

死机Dump信息获取¶
DebugKits工具提供了死机发生时,将死机现场内存以二进制格式导出功能,提供参考信息,帮助开发者定位分析死机问题。工具的详细安装使用请参考《BS2XV100 DebugKits工具使用指南》,此处只介绍Dump信息导出流程。
首先进入工具页面点击options再选择change chip,在弹框中选择BS2X确定芯片类型,参考图选择芯片。

然后点击Connection选择Connect在弹窗中选择连接方式以及对应的通道序号进行连接,参考图连接芯片。

如果是第一次连接芯片或者芯片有新版本的程序,需要更新HDB并重新连接之后,日志的打印才能正确的显示,在Option菜单下选择Update HDB,选择生成的database目录(\output\bs2x\database_evb)再点击OK即可完成配置,具体参考下图:配置database。

通过DebugKits工具可以实时观测芯片运行时打印的具体信息,可以参考图 DebugKits日志打印。当死机发生时Message视图将会打印出last word以及last dump信息。

last word信息是死机发生时,将PC地址、返回地址、栈地址、CPU寄存器信息发送出来,用户可以根据last word的具体信息分析错误类型、定位错误原因,具体可以参考下图:last word信息。

last dump信息将会把内存中的部分信息打包生成bin文件存储到DebugKits的安装目录中的DumpInfo子目录之下,具体参考下图:last dump生成文件。

说明: 注意:将内存dump到DebugKits中需要一定时间(1~2 min),因此如果需要使用last dump分析死机信息,不要在发生死机故障后立即复位,否则last dump信息可能无法完整获取。
连接J-Link调试器导出¶
在允许连接JTAG调试器的场景下,用户可连接J-Link仿真器等工具进一步获取死机现场的相关信息。以下主要介绍通过J-Link仿真器获取死机相关信息的过程。
本小节主要介绍在死机现场使用J-Link调试器导出死机的过程。
使用J-Link仿真器调试需先连接芯片SWD管脚,具体位置以实际产品为准。硬件连接后,执行工具包(sdk\tools\bin\jlink_tool\bs2x)中的Commandline_bs2x_mcpu.bat脚本,脚本将自动进行连接(其他核的连接操作可参照此方法进行)。
J-Link连接MCPU示意图

连接J-Link后可以使用Jlink命令调试芯片,获取芯片运行信息,Jlink常用命令如下:J-Link常用命令
命令 |
描述 |
|---|---|
con/connect |
连接。 |
h/halt |
暂停,停止。 |
g/go |
继续,运行。 |
mem32 mem16 mem8 |
I/O读32bit:mem32 <Addr>, <NumItems> (hex) (Addr表示内存地址,NumItems表示从Addr开始连续读几个32bit ) I/O读16bit:mem16 <Addr>, <NumItems> (hex) I/O读8bit:mem8 <Addr>, <NumItems> (hex) |
w4 w2 w1 |
I/O写4Byte:w4 <Addr>, <Data> (hex) I/O写2Byte:w2 <Addr>, <Data> (hex) I/O写1Byte:w1 <Addr>, <Data> (hex) |
readcsr |
读riscv csr寄存器:ReadCSR <RegIndex> |
writecsr |
写riscv csr寄存器:WriteCSR <RegIndex>,<Value> |
查询信息步骤如下:
查询CPU寄存器信息,如图 查询CPU寄存器值所示。

查询内存信息,获取维测变量值或普通变量值。
在\output\bs2x\xxx_core\xxx_bs2x_xxx\xxx.nm中获取nm文件,在nm文件中获取想要查询的变量地址如图 变量地址中“g-exception-dump-callback”变量的地址为0x20025950。

通过J-Link获取状态信息,如图 变量值。

说明: RISC-V架构处理器需要使用V10及以上版本的J-Link仿真器。 芯片连接J-Link需要保证处于工作状态,睡眠状态时无法连接。
狗超时重启¶
狗超时重启分为:CWDT、ULP WDT 重启。其中APP发生狗超时(CWDT超时),会有HSO日志和UART日志打印,显示对应的pc和lr,并且会导出内存信息。
APP核发生狗超时时,系统会进行重启。系统重启后,串口会打印异常时的pc和lr,如图1所示。HSO日志也会打印重启原因0x2008及对应的pc和lr,如图2所示。可以在lst文件中查找pc值确定问题归属。APP核的pc指针范围如表3-1所示。
表 1 APP代码空间范围
内存名称 |
开始地址 |
结束地址 |
|---|---|---|
ITCM |
0x42000 |
0x56000 |
DTCM |
0x20000000 |
0x20010000 |
SFC |
0x90100000 |
0x90180000 |
图 1 CWDT超时重启UART打印

图 2 CWDT超时重启HSO日志打印

ULP_WDT超时¶
APP进入睡眠时,CWDT将掉电,WDT功能由ULP_WDT实现。发生狗超时时,系统会进行重启。系统重启后,串口和HSO日志也会打印重启原因0x200f,如图1所示。
图 1 WDT超时重启UART打印

hardfault、MEM fault、BUS fault、Usage fault异常¶
系统发生上述异常时,HSO日志会记录异常时的pc、lr和mtval,如图1所示。
系统重启后,也会打印重启原因及对应的pc、lr和mtval,如图2所示。。
APP核异常¶
APP核发生上述重启会打印重启原因0x2004。
图 1 APP hardfault重启HSO日志

图 2 APP hardfault重启UART打印

NMI异常¶
系统发生如下NMI异常时,重启后会打印如下重启原因。HSO日志会记录pc、lr和mtval。
XIP异常¶
系统发生XIP重启会打印重启原因0x2009。HSO日志会输出pc、lr和mtval。如图1和图2所示
图 1 XIP重启HSO日志

图 2 XIP重启UART打印

XIP CACHE异常¶
系统发生XIP cache异常会打印重启原因0x200a。HSO日志会记录pc、lr和mtval。如图1和图2所示
图 1 XIP cache异常HSO日志

图 2 XIP cache异常UART打印

MDMA异常¶
系统发生MDMA异常会打印重启原因0x200b。HSO日志会记录pc、lr和mtval。如图1和图2所示
图 1 MDMA异常HSO日志

图 2 MDMA异常UART打印

SMDMA异常¶
系统发生SMDMA异常会打印重启原因0x200c。HSO日志会记录pc、lr和mtval。如图1和图2所示
图 1 SMDMA异常HSO日志

图 2 SMDMA异常UART打印

A核NMI异常¶
A核发生NMI异常会打印重启原因0x2005。HSO日志会记录pc、lr和mtval。如图1和图2所示
图 1 A核NMI异常HSO日志

图 2 A核NMI异常UART打印

panic异常¶
发生panic异常时,会打印panic id,code以及调用的lr。并获取对端的pc,lr。其中normal core表示对端。Normal core为0表示A核异常。Normal core为1表示bt核异常。
APP PANIC异常¶
APP Panic异常会打印重启原因0x2003和panic日志。如图1和图2所示。
图 1 panic异常HSO日志

图 2 panic异常UART打印

PANIC ID¶
表 1 PANIC ID描述(middleware/utils/dfx/panic/public/panic.h)
id(十进制值) |
描述 |
备注 |
|---|---|---|
0 |
no panic |
NO USE |
1 |
arm core |
NO USE |
2 |
HAL |
NO USE |
3 |
DSP |
NO USE |
4 |
malloc |
BT/APP |
5 |
uart |
BT/APP |
6 |
flash |
APP |
7 |
adc |
APP |
8 |
watchdog |
NO USE |
9 |
log |
BT/APP |
10 |
dma |
BT/APP |
11 |
assert |
BT/APP |
12 |
reboot |
NO USE |
13 |
update |
NO USE |
14 |
I2C0 |
APP |
15 |
I2C1 |
APP |
16 |
I2C2 |
APP |
17 |
SPI0 |
APP |
18 |
SPI1 |
APP |
19 |
SPI2 |
APP |
20 |
SPI4 |
APP |
21 |
SPI5 |
APP |
22 |
RPC |
BT/APP |
23 |
mem monitor |
BT/APP |
24 |
panic exit |
NO USE |
25 |
create task |
liteOS |
26 |
memcpy buffer overlap |
NO USE |
27 |
xip |
APP |
28 |
btc malloc fail |
BTC |
29 |
low power |
BT/APP |
30 |
system status |
BT/APP |
31 |
Btc int err |
BTC |
32 |
Lpc wakeup fail |
APP |
33 |
Clock glb err |
BT/APP |
34 |
Pmu cmu |
APP |
35 |
Pmu ldo |
APP |
36 |
Chip wdt frst |
APP |
37 |
Non os |
NO USE |
38 |
Lpc veto |
BT/APP |
39 |
timer |
BT/APP |
40 |
lpc |
BT/APP |
41 |
rtc |
BT/APP |
42 |
memory |
BT/APP |
43 |
Cpu hifi |
APP |
44 |
Exception test |
APP |
45 |
kv |
SSB ONLY |
46 |
eflash |
APP |
47 |
lib |
NO USE |
48 |
codeloader |
SSB ONLY |
49 |
criticla |
BT/APP |
50 |
ipc |
BT/APP |
51 |
int |
BT/APP |
52 |
os |
liteOS |
53 |
Lpc wakeup time |
BT/APP |
54 |
btc bt frm dismatch1 |
BTC |
55 |
btc bt frm dismatch2 |
BTC |
56 |
btc ble frm dismatch |
BTC |
57 |
btc wakeup |
BTC |
58 |
bth memory |
BTH |
59 |
mpu config |
BT/APP |
60 |
gpu |
wear APP |
61 |
sdio |
wear APP |
62 |
ir |
wear APP |
63 |
btc cmd |
BTC |
64 |
cap |
APP |
65 |
epmu |
APP |
66 |
bts writeproperty |
BTS |
67 |
mmc host |
wear APP |
68 |
btc oscen |
BTC |
69 |
I2C3 |
APP |
70 |
RGB888 |
wear APP |
71 |
ap commu |
BTC |
72 |
Cpu clocks |
BT/APP |
73 |
clocks switch |
APP |
74 |
pmu |
NO USE |
75 |
log dump |
BT/APP |
76 |
coul |
APP |
77 |
dsp0 power off |
BT/APP |
78 |
dsp1 power off |
BT/APP |
98 |
UT |
PLT UT |
99 |
testsuite |
PLT UT |
强制狗复位¶
系统发生强制狗复位时,不会主动触发hardfault或者nmi异常。等待芯片内部的狗超时后,芯片会主动触发复位。芯片复位后,检测到复位原因0x200d,并触发一次panic异常,系统会导出上次的cpu trace并触发重启。系统重启后,会打印panic重启,重启原因:0x2003;panic id:0x24。如图1所示。
图 1 ULP RTC超时重启打印

问题定位¶
外设问题定位方法¶
外设类异常,绝大部分都是初始化没有完成导致的异常。
外设初始化主要由以下步骤:
上电、开时钟。
配置管脚模式。
注册hal层回调函数。
注册回调。
初始化寄存器配置。
排查思路:
首先确认时钟/电源是否打开,当前默认大部分驱动时钟都是关闭的,使用前,查看XXX驱动初始化代码中,CONFIG_XXX_SUPPORT_LPC宏是否打开,如果没有打开,访问XXX驱动相关的寄存器会立刻挂死。
查看uapi_xxx_init返回值,确认初始化是否完成。
软件流程排查(请参见《BS2XV100 设备驱动开发指南》)。
排查硬件:上电是否正常,外围电流是否正常,同样的软件在不同硬件上烧录,查看结果是否一致。
其他外设问题,查到外设说明。
死机问题定位方法¶
图 1 定位步骤

低功耗问题定位方法¶
管脚漏电排查¶
睡眠状态下,芯片功耗不达标,需要用户按照如下流程排查IO漏电:
对于板级电路IO管脚:
若管脚有外接上拉电阻,该管脚需配置为浮空。
管脚有外接下拉电阻,该管脚可配置为下拉。
其余情况,该管脚配置为下拉。
对于接入外部设备的管脚:
请查阅该外设手册,若外设内部带上拉/下拉电阻,根据上述说明进行配置。
其他问题可以参考《BS2XV100 低功耗FAQ》。
OSAL接口维测方法¶
BS2X SDK提供了OSAL接口帮助用户使用RTOS,用户在使用接口前请参见《BS2XV100 SDK 开发指南》,避免出现接口使用错误,客户在使用OSAL接口开发时,可以通过判断OSAL接口返回值或者打印OSAL接口返回值进行维测。OSAL接口错误码请参考《BS2XV100 SDK 开发指南》。
内存维测功能¶
内存的维测在任何一个系统中都是重要的维测手段,比如:想知道系统中,当前内存的分配和占用情况;想知道各个task的栈占用情况;想知道某个task内存分配具体分配情况。这些内存信息对于内存优化、以及内存定位(比如内存泄露)至关重要。
本文描述如何在B2X系列上如何进行内存维测,包括:
查看系统heap内存总体分配和使用情况。
查看每个task的malloc情况(当前分配和峰值分配)。
查看每个task的栈分配和使用情况;(当前使用和峰值使用)。
查看每个内存在哪里分配。
AIOT的内存维测命令已经集成到AT命令中,用户可以直接使用这些命令。
查看系统heap总体信息¶
该维测功能,用来查看系统中heap内存使用看情况;
查看系统heap信息命令:AT+HEAPSTAT

查看task栈使用情况¶
该维测功能,用来查看各个Task的栈的分配使用使用情况;
可以通过该命令,来优化调整stack的分配大小,节约内存;
查看任务栈使用情况:AT+TASKSTACK

查看每个task的内存分配情况¶
查看某个task的malloc情况:AT+TASKMALLOC=“任务id”;
这里的“任务id”,可以通过“AT+HEAPSTAT”或“AT+TASKSTACK”命令获取。

查看中断中、启动阶段的内存分配情况¶
系统中,除了task中分配内存外,还有中断中分配的内存、系统启动阶段的分配的内存,这些地方分配的地方没有对应的task,因此通过归类到一个特殊id中;
可以通过“AT+HEAPSTAT” 命令,获取non-task alloc对应的id;
查看非任务中内存分配情况的命令,也是:AT+TASKMALLOC=“id”,显示格式也是一样;

内存维测开关¶
由于内存维测需要消耗一定内存空间,默认情况下是关闭的;因此需要手动打开;
以BS21E为例,开启内存维测功能需要打开下面两个宏:打开“kernel\liteos\liteos_v208.6.0_b017\Huawei_LiteOS\tools\build\config\bs21e.config”,将
# LOSCFG_MEM_TASK_STAT is not set
# LOSCFG_MEM_DFX_SHOW_CALLER_RA is not set
改成:
LOSCFG_MEM_TASK_STAT=y
LOSCFG_MEM_DFX_SHOW_CALLER_RA=y”
注意事项
说明:
必须要找对项目对应的config文件。
LOSCFG_MEM_DFX_SHOW_CALLER_RA用来查看在哪里调用了内存分配的函数,该宏打开会占用一定内存空间;如果只是进行内存统计,可以不打开。
功能开启后需要对SDK进行全量编译;否则可能会有不生效的情况。
OS及中断维测¶
维测原理¶
使用LiteOs提供的OSCFG_HWI_PRE_POST_PROCESS和LOSCFG_BASE_CORE_TSK_MONITOR(liteos menuconfig中默认开启)特性,记录中断和任务调用轨迹。该功能默认不开启,用户使用的时候,需要手动开启OS_DFX_SUPPORT和USER_PRINT_OS_DFX两个宏。
注意:
开启该功能,可能会导致部分任务栈空间增加,导致死机(参考“案例一 栈溢出”),需要调整对应任务栈大小。
开启该功能,会导致无法中断频繁打印,不需要观测的中断,可以手动关闭。参考下图,关闭不需要观测的中断。

该功能,可能会造成一些性能问题,定位狗超时问题时,可以临时打开该功能。
维侧日志解析¶
中断维测:
下面的维测表示进入75号中断处理函数(hwi_pre表示);75号中断处理期间,又被66号中断打断(支持中断嵌套,66优先级更高),66号中断连续处理两次后,75号中断无法退出,大概率是中断栈爆了,需要修改中断栈大小。参考下图:


任务调度维测:

锁中断时间与中断时间维测¶
维测原理¶
使用osal提供的锁中断时间和中断时间维测特性,记录锁中断时间和中断时间。该功能默认不开启,用户使用的时候,需要手动添加osal_adapt组件和开启OSAL_IRQ_RECORD_DEBUG这个宏。通过osal_irq_record_flag_set函数参数设置1统计锁中断时间,参数设置2统计中断时间。
注意:
开启该功能,可能会导致部分任务栈空间增加,导致死机(参考“案例一 栈溢出”),需要调整对应任务栈大小。
开启该功能,不需要观测的中断,可以手动关闭。参考下图,关闭不需要观测的中断。

在app_os_init中使用维测时间打印函数,将记录的时间打印出来。


维测日志解析¶

查询系统CPU占用率维测功能¶
CPU占用率的统计是分析系统负载的重要的维测手段,比如:通过CPU占用率判断当前系统负载是否超出设计规格;通过系统中各个任务的CPU占用率,判断各个任务的CPU占用率是否符合设计的预期。
查看各个任务和中断的CPU占用情况¶
可以通过该命令,查看各个Task和中断的CPU占用率情况:AT+CPUP

说明:
CPU占用率是以百分比显示。
CPU占用率的计算方法:任务(或者中断)运行总时间/系统运行总时间.
CPUP列显示系统启动至今总的CPU占用率。
CPUP 10.0s列显示系统最近10s的CPU占用率。
CPUP 1.0s列显示系统最近1s的CPU占用率。
维测开关¶
CPU占用率统计对性能有影响,一般只在开发阶段时需要了解各个任务的占用率,因此默认情况该功能是关闭的;如果要使用该功能需要手动打开。
以BS21E为例,打开“kernel\liteos\liteos_v208.6.0_b017\Huawei_LiteOS\tools\build\config\bs21e.config”,CPUP维测功能需要打开下面宏:
LOSCFG_KERNEL_EXTKERNEL=y
LOSCFG_KERNEL_CPUP=y
LOSCFG_CPUP_INCLUDE_IRQ=y
LOSCFG_CPUP_SAMPLE_PERIOD=1000
LOSCFG_CPUP_HISTORY_RECORD_NUM=10
LOSCFG_DEBUG_VERSION=y
LOSCFG_DEBUG_KERNEL=y
LOSCFG_DEBUG_TASK=y
LOSCFG_DEBUG_HWI=y
注意事项
说明:
必须要找对项目对应的config文件。
功能开启后需要对SDK进行全量编译;否则可能会有不生效的情况。
关闭配置项LOSCFG_CPUP_INCLUDE_IRQ后,系统中的中断耗时会被统计到中断发生的任务中,即被中断打断的任务中。
典型案例¶
案例一 栈溢出¶
栈溢出
死机现场如下:


死在OsHeapAlloc,那么可在下图位置加日志,在死机时把栈统计打出来:

打印结果如下:

可以得出app栈爆结论,需要加大线程栈大小。
案例二 通过MEPC分析¶
根据mepc分析死机
死机现场如下:

问题分析
挂死在“app”中,且可以看到mepc为0x90122458,mtval为:0xffffffff。
在lst中找到mepc所对应的汇编代码。

确认是软件操作非法地址writel(0xffffffff, 0)导致的死机。
案例三 通过RA分析¶
PC异常,根据RA分析死机
死机现场如下:

问题分析
挂死在“Swt_Task”中,且pc非法,只能看到ra。
根据lst文件,发现在跳转“g_hal_funcs->set_group”的异常。

怀疑是g_hal_funcs->set_group为空导致的,继续查看lst文件中,pwm初始化代码。发现只有ir中有调用,而且出现问题的时候,没有执行红外学习。

找客户确认,客户的确把pwm初始化代码删掉。添加初始化代码后,问题解决。
问题拓展
如果客户判断一下uapi_pwm_open返回值,就能快速发现这个问题。
软件代码可以增加判断,异常不让系统挂死。

为什么挂死的时候,pc会变成deadbeee?
注意:芯片把0x0~0x10000初始化为0xDEADBEEF,mepc的bit0 硬控为0


分析:汇编代码g_hal_funcs

g_hal_funcs是bss段,初始化为0,没有赋值的时候g_hal_funcs里面的值是0
汇编流程

案例四 访问死机¶
软件执行过程中因为代码逻辑或者内存踩踏等问题,常常造成部分数据变成空地址或者其他非法地址,访问时出现死机。本案例通过实例分析,结合BS2X提供的死机维测方式分析定位空指针执行。如图执行空指针造成的死机log:

首先参考普通打印口维测信息获取章节分析死机现场,当前mcause为1,参考分析mcause对照表,死机原因是指令访问错误,同时mepc、ra均被设置为难以通过application.lst或者application.asm汇编文件直接追溯的地址值,对于此类问题,通常使用CPU Trace工具解析串口Trace,分析死机前执行流程,使用工具解析串口Trace,进入异常前,最后一个执行的函数为plt_at_dumptest:

通过application.lst文件查找plt_at_dumptest函数偏移0x10位置,此处进行了空指针访问操作,造成挂死。

总结:对于mcause为0x0、0x1、0x2等类型的指令访问挂死,在死机现场的寄存器难以追溯异常位置时,使用串口Trace解析工具解析Trace,结合application.lst文件追溯死机流程。
案例五 内存Malloc失败¶
代码执行过程中如果出现panic内存不足以及malloc失败,可以借助当前的内存维测接口进行维测:

参考“内存维测开关”章节,打开内存维测开关,打开之后进入panic时就会打印内存使用信息,如下图在打开内存维测之后,内存申请不足时,维测信息内会打印各个线程内存使用信息,以及死机时各个线程的内存信息。

参考下图,app线程peak峰值异常,根据内存详细使用信息,可以看到0x4523e地址频繁申请内存。

结合lst文件观察调用位置,可以看到异常调用函数异常申请,没有释放。
常见问题FAQ¶
异常信息查看¶
异常信息汇总¶
表 1 异常信息描述
成员 |
描述 |
|---|---|
task |
死机的任务名称。 |
thrdPid |
死机的任务ID。 |
type |
死机类型。 |
CPU寄存器信息¶
表 1 死机相关CPU寄存器描述
成员 |
描述 |
|---|---|
mepc |
机器异常程序计数器。当发生异常时,mepc指向导致异常的指令;对于中断,mepc指向中断处理后应该恢复的位置。 |
mstatus |
机器状态寄存器。 |
mtval |
机器陷入寄存器。保存地址异常中出错的地址或者发生指令异常的指令本身,对于其他错误,其值为零。 |
mcause |
机器异常寄存器。保存目前异常或者中断的原因,通过查询表 mcause&ccause异常描述表得到目前异常或者中断的类型。 |
ccause |
与mcause类似,ccause为mcause的补充说明,对于某些异常通过读取ccause寄存器的内容可以进一步明确异常类型。 |
ra |
返回地址。 |
表 2 mcause&ccause异常描述表
异常码 |
mcause异常描述 |
ccause异常描述 |
|---|---|---|
0x0 |
Instruction address misaligned |
Not available |
0x1 |
Instruction access fault |
Memory map region access fault |
0x2 |
Illegal instruction |
AXIM error response |
0x3 |
Breakpoint |
AHBM error response |
0x4 |
Load address misaligned |
Crossing PMP entries |
0x5 |
Load access fault |
System register access fault |
0x6 |
Store/AMO address misaligned |
No PMP entry matched |
0x7 |
Store/AMO access fault |
PMP access fault |
0x8 |
Environment call from U-mode |
CMO access fault |
0x9 |
Environment call from S-mode |
CSR access fault |
0xa |
Reserved |
LDM/STMIA instruction |
0xb |
Environment call from M-mode |
ITCM write access fault |
0xc |
Instruction page fault |
Not available |
0xd |
Load page fault |
Not available |
0xe |
Reserved |
Not available |
0xf |
Store/AMO page fault |
Not available |
>0xf |
Reserved |
Not available |
通用寄存器信息¶
表 1 通用寄存器描述
寄存器 |
ABI 名字 |
保存者 |
作用 |
|
|---|---|---|---|---|
整型通用寄存器 |
x0 |
zero |
— |
硬编码恒为0,写入数据忽略,读取永远为0 |
x1 |
ra |
Caller(调用者) |
函数调用的返回地址 |
|
x2 |
sp |
Callee(被调用者) |
栈指针 |
|
x3 |
gp |
— |
全局指针 |
|
x4 |
tp |
— |
线程指针 |
|
x5-x7 |
t0-2 |
Caller |
临时寄存器 |
|
x8 |
s0/fp |
Callee |
需要保存的寄存器或者帧指针寄存器 |
|
x9 |
s1 |
Callee |
需要保存的寄存器,保存原进程中的关键数据,避免在函数调用过程中被破坏 |
|
x10~x11 |
a0~1 |
Caller |
函数参数/返回值 |
|
x12~x17 |
a2~7 |
Caller |
函数参数 |
|
x18~x27 |
s2~11 |
Callee |
需要保存的寄存器 |
|
x28~x31 |
t3~6 |
Caller |
临时寄存器 |
|
浮点型通用寄存器 |
f0~f7 |
ft0~7 |
Caller |
FP临时变量 |
f8~f9 |
fs0~1 |
Callee |
FP保存的寄存器 |
|
f10~f11 |
fa0~fa1 |
Caller |
FP参数/返回值 |
|
f12~f17 |
fa2~fa7 |
Caller |
FP参数 |
|
f18~f27 |
fs2~fs11 |
Callee |
FP保存的寄存器 |
|
f28~f31 |
ft8~ft11 |
Caller |
FP临时变量 |
|
ADC常见问题排查¶
采样挂死
采样挂死有三种常见原因:
时钟电源被异常关闭:ADC上电时已经打开需要的时钟,如果进入低功耗或在其他业务流程中关闭了需要的时钟,可能导致校准采样等无法完成。需要排查的时钟包括:XO_2_AFE 0x57008410 bit4;XO_2_ADC 0x57008410 bit1。需要排查的电源包括NFCLDO 0x5702C230 bit0:3 配置为0xA;AFELDO,ADCLDO,VREFLDO。如果使用ADC进行音频采样,还需要打开电源MICLDO并配置为2.0V。
AMIC与GADC复用,因为音频采样与电压采集复用的是同一套电路,所以需要分时复用。
ADC采样未加锁中断,可能因为被其他中断打断导致采样挂死。
采样值异常
采样值异常有多重原因和表现形式,下面枚举一些典型场景及排查方式:
采样值与实际值误差比例恒定,即采样值越大偏差越大:因为BS2X使用的ADC量程为0~1.5V,常常使用分压电路将待测电压分压后传入ADC,分压电阻的精度可能引入误差,要求使用1%精度的电阻来降低偏差,且转换公式要严格按照电阻值带入,切莫做近似处理,因为不支持浮点运算,建议先乘法再除法。
首次上电、睡眠唤醒后采样值偏低:如上文所述,分压电路中一般存在对地电容,实测100nF电容充电到稳定的时长约为60ms。
使用记录好的校准值采样时,采集到的电压不准确:因为刷入记录好的校准值替换的是校准的流程,降低了大约180ms的启动时间,在此期间电路可能还未稳定,如果使用记录好的校准值,建议延时10~20ms再采样。
排查方式:
ADC有一组较为关键的校准值,存储在0x570360D4~0x570360F4共9位,其默认值为:
0x00008000 0x00004000 0x00002000 0x00001000 0x00000800 0x00000800 0x00000400 0x00000200
一般校准的结果与默认值的绝对差值在0x20以内,偏差较大则表明校准失常,如果全部为默认值则表示未进行校准。多次校准出的校准值可能不同,但相差极小。
ADC的三路LDO有各自的trim值存在efuse中,初始化时会将其读取并写入ADC,可以比对两组地址里的值是否相等:
efuse: 0x5702886C[0-3], AFELDO_trim: 0x570363F0[4-7]
efuse: 0x5702886C[4-7], ADCLDO_trim: 0x570363E8[4-7]
efuse: 0x5702886C[8-11], VREFLDO_trim: 0x570363F4[5-8]




