摘要:本篇介绍下鸿蒙轻内核中异常钩子模块发生系统中断异常时如何转储异常信息。

本文分享自华为云社区《鸿蒙轻内核M核源码分析系列十七(3) 异常信息ExcInfo》,作者: zhushy。

ExcHook异常钩子模块是OpenHarmony LiteOS-M内核的一个可选组件,提供注册钩子函数LOS_RegExcHook、解除注册钩子函数LOS_UnRegExcHook等操作接口。发生系统时,支持保存异常上下文、任务信息、队列信息、中断寄存器状态、任务切换信息、内存分配等信息。由于异常钩子模块内容较多,我们分为几篇进行分析源码,分别介绍异常钩子函数的类型,如何注册和解除注册钩子函数,如何转储异常信息等。本篇介绍下异常钩子模块发生系统中断异常时如何转储异常信息。

本文中所涉及的源码,以OpenHarmony LiteOS-M内核为例,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。鸿蒙轻内核异常钩子模块代码主要在components\exchook目录下。

1、异常信息的宏定义、枚举和结构体

在文件components\exchook\los_exc_info.h定义了异常信息的相关宏定义、枚举和结构体。如下所示的宏定义为各种异常信息的大小,可以参考下面的异常信息存储区域分布图进行直观的理解,前4字节保存异常信息存储区域的大小,然后分别是异常上下文、任务、队列,中断寄存器,任务切换,内存分配情况的数据信息,最后4字节保存的是异常类型的最大值。

异常上下文存储区域的详细分布如下图所示,保存异常信息类型和信息大小,然后分别存储ExcInfo和上下文信息。其他异常信息类似,不再提供。

#define INFO_TYPE_AND_SIZE      8#define MAX_SCENE_INFO_SIZE     (INFO_TYPE_AND_SIZE + sizeof(ExcInfo) + sizeof(EXC_CONTEXT_S))
#define MAX_TSK_INFO_SIZE       (INFO_TYPE_AND_SIZE + sizeof(TSK_INFO_S) * (LOSCFG_BASE_CORE_TSK_LIMIT + 1))#if (LOSCFG_BASE_IPC_QUEUE == 1)
#define MAX_QUEUE_INFO_SIZE     (INFO_TYPE_AND_SIZE + sizeof(QUEUE_INFO_S) * LOSCFG_BASE_IPC_QUEUE_LIMIT)
#else
#define MAX_QUEUE_INFO_SIZE     (0)
#endif#if (LOSCFG_BASE_CORE_EXC_TSK_SWITCH == 1)
#define MAX_SWITCH_INFO_SIZE    (INFO_TYPE_AND_SIZE + (sizeof(UINT32) + sizeof(CHAR) * LOS_TASK_NAMELEN) * OS_TASK_SWITCH_INFO_COUNT)
#else
#define MAX_SWITCH_INFO_SIZE    (0)
#endif#define MAX_MEM_INFO_SIZE       (INFO_TYPE_AND_SIZE + sizeof(MemInfoCB) * OS_SYS_MEM_NUM)
#define MAX_EXC_MEM_SIZE        (INFO_TYPE_AND_SIZE + MAX_SCENE_INFO_SIZE + MAX_TSK_INFO_SIZE + MAX_QUEUE_INFO_SIZE + MAX_INT_INFO_SIZE + MAX_SWITCH_INFO_SIZE + MAX_MEM_INFO_SIZE)

从文件中定义的枚举,支持的异常信息类型包含上下文、任务、对外、中断寄存器、任务切换和内存分配信息。枚举定义如下:

typedef enum {OS_EXC_TYPE_CONTEXT     = 0,OS_EXC_TYPE_TSK         = 1,OS_EXC_TYPE_QUE         = 2,OS_EXC_TYPE_NVIC        = 3,OS_EXC_TYPE_TSK_SWITCH  = 4,OS_EXC_TYPE_MEM         = 5,OS_EXC_TYPE_MAX         = 6
} ExcInfoType;

2、异常信息初始化

在文件kernel\src\los_init.c中的函数UINT32 LOS_KernelInit(VOID)内会调用OsExcMsgDumpInit()函数进行初始化,代码片段如下。该初始化代码被宏LOSCFG_PLATFORM_EXC包围,需要开启该宏才能生效。

#if (LOSCFG_PLATFORM_EXC == 1)OsExcMsgDumpInit();
#endif

在分析函数OsExcMsgDumpInit代码之前,我们先看下函数OsExcRegister的代码。函数比较简单,⑴处的g_excArray[]异常信息转储函数数组,支持发生异常时调用这些函数存储任务、内存、中断寄存器等信息,⑵处标记异常信息转储函数是否有效,每个类型的异常信息转储函数只能设置一次。

VOID OsExcRegister(ExcInfoType type, EXC_INFO_SAVE_CALLBACK func, VOID *arg)
{ExcInfoArray *excInfo = NULL;if ((type >= OS_EXC_TYPE_MAX) || (func == NULL)) {PRINT_ERR("HalExcRegister ERROR!\n");return;}
⑴  excInfo = &(g_excArray[type]);if (excInfo->valid == TRUE) {return;}excInfo->type = type;excInfo->fnExcInfoCb = func;excInfo->arg = arg;
⑵  excInfo->valid = TRUE;
}

函数OsExcMsgDumpInit代码定义在components\exchook\los_exc_info.c,代码如下所示。⑴处的OS_SYS_MEM_NUM来自kernel\include\los_config.h配置文件,在记录内存信息时,会记录每个内存块内存节点的信息,该配置数值表示可以记录的内存块内存节点的数量。

⑵处的g_excMsgArray是个字节数组用于存储异常信息,g_excContent是执行字节数组的指针,存储异常信息时该指针不断指向字节数组的后面的位置。⑶处开始的代码调用OsExcRegister()函数分别设置上下文、任务、队列、中断、任务切换、内存等异常信息转储函数。具体的异常信息转储函数在后文分析。⑷处代码为中断异常类型注册异常钩子函数OsExcMsgDump()。

VOID OsExcMsgDumpInit(VOID)
{g_excQueueMaxNum = LOSCFG_BASE_IPC_QUEUE_LIMIT;
⑴  g_excMemMaxNum = OS_SYS_MEM_NUM;
⑵  g_excContent = (VOID *)g_excMsgArray;⑶  OsExcRegister(OS_EXC_TYPE_CONTEXT, OsExcContentGet, NULL);OsExcRegister(OS_EXC_TYPE_TSK, OsExcTaskMsgGet, &g_taskMaxNum);
#if (LOSCFG_BASE_IPC_QUEUE == 1)OsExcRegister(OS_EXC_TYPE_QUE, OsExcQueueMsgGet, &g_excQueueMaxNum);
#endifOsExcRegister(OS_EXC_TYPE_NVIC, OsExcSaveIntStatus, NULL);
#if (LOSCFG_BASE_CORE_EXC_TSK_SWITCH == 1)OsExcRegister(OS_EXC_TYPE_TSK_SWITCH, OsExcTskSwitchMsgGet, &g_taskSwitchInfo);
#endifOsExcRegister(OS_EXC_TYPE_MEM, OsExcMemMsgGet, &g_excMemMaxNum);⑷  (VOID)LOS_RegExcHook(EXC_INTERRUPT, (ExcHookFn)OsExcMsgDump);
}

3、中断异常钩子函数OsExcMsgDump

函数OsExcMsgDump()是注册的对应中断异常类型的异常钩子函数。当发生中断异常时,会执行该函数转储异常信息到g_excMsgArray数组,转储前执行⑴把该内存区域初始化为0xFF。⑵处把转储区的前4个字节存储异常信息的大小,然后g_excContent指针往后移动4个字节。然后遍历g_excArray[]异常信息转储函数数组循环执行,会依次都各类信息转储到g_excMsgArray数组。转储信息后执行⑷把指定区域设置异常信息类型的最大值,然后g_excContent指针往后移动4个字节。

STATIC VOID OsExcMsgDump(VOID)
{UINT32 index;/* Ignore the return code when matching CSEC rule 6.6(4). */
⑴  (VOID)memset_s(g_excMsgArray, g_excArraySize, EXC_MSG_ARRAY_INIT_VALUE, g_excArraySize);⑵  *((UINT32 *)g_excContent) = MAX_EXC_MEM_SIZE;  /* The total length of exception information. */g_excContent = (UINT8 *)g_excContent + sizeof(UINT32);for (index = 0; index < OS_EXC_TYPE_MAX; index++) {if (!g_excArray[index].valid) {continue;}
⑶      g_excArray[index].fnExcInfoCb(g_excArray[index].type, g_excArray[index].arg);}⑷  *((UINT32 *)g_excContent) = OS_EXC_TYPE_MAX;g_excContent = (UINT8 *)g_excContent + sizeof(UINT32);return;
}

4、支持的异常信息转储函数

从枚举类型ExcInfoType,可以得知支持转储的异常信息有6类,对应的转储函数在VOID OsExcMsgDumpInit(VOID)函数中进行注册。我们挑2个简单看下这些转储函数是如何工作。

4.1 OsExcContentGet上下文转储

上下文转储是第一块要转储的信息,保存异常上下文信息。⑴处获取存储区域的结束地址。⑵处存储异常信息类型,然后g_excContent指针往后移动4个字节。⑶处存储信息大小,然后g_excContent指针往后移动4个字节。⑷处把g_excInfo异常信息复制到存储区域当前指向的位置,其中excContentEnd - (UINTPTR)g_excContent用于保证复制不会越界溢出,然后继续后移指针。⑸处转储上下文信息,然后继续后移指针,完成上下文信息转储。

STATIC UINT32 OsExcContentGet(UINT32 type, VOID *arg)
{
⑴  UINTPTR excContentEnd = MAX_EXC_MEM_SIZE + (UINTPTR)g_excMsgArray;errno_t ret;(VOID)arg;/* save exception info */
⑵  *((UINT32 *)g_excContent) = type;g_excContent = (UINT8 *)g_excContent + sizeof(UINT32);
⑶  *((UINT32 *)g_excContent) = sizeof(ExcInfo) + sizeof(EXC_CONTEXT_S);g_excContent = (UINT8 *)g_excContent + sizeof(UINT32);⑷  ret = memcpy_s((VOID *)g_excContent, excContentEnd - (UINTPTR)g_excContent,(VOID *)&g_excInfo, sizeof(ExcInfo));if (ret != EOK) {return LOS_NOK;}g_excContent = (UINT8 *)g_excContent + sizeof(ExcInfo);⑸  ret = memcpy_s((VOID *)g_excContent, excContentEnd - (UINTPTR)g_excContent,g_excInfo.context, sizeof(EXC_CONTEXT_S));if (ret != EOK) {return LOS_NOK;}g_excContent = (UINT8 *)g_excContent + sizeof(EXC_CONTEXT_S);return LOS_OK;
}

4.2 OsExcSaveIntStatus中断寄存器信息转储

OsExcSaveIntStatus()函数用于转储中断寄存器的数据,⑴、⑵和⑶和其他转储函数类似,分别是获取存储区域的结束地址,设置类型和大小信息,并后移g_excContent指针。⑷处的OS_NVIC_SETENA_BASE定义在kernel\arch\arm\cortex-m7\gcc\los_arch_interrupt.h,是Interrupt enable register中断使能寄存器的地址,它的大小由OS_NVIC_INT_ENABLE_SIZE定义。后续的代码分别转储其他中断寄存器,比如Interrupt Set-Pending Registers中断设置请求寄存器的地址OS_NVIC_SETPEND_BASE、Interrupt Active Bit Register中断活跃寄存器的地址OS_NVIC_INT_ACT_BASE、Interrupt Priority Register中断优先级寄存器的地址OS_NVIC_PRI_BASE,这些中断寄存器可以查看官网了解更多,或者查看下图。

⑸处的代码是System Handler Priority Register系统处理优先级寄存器的地址OS_NVIC_EXCPRI_BASE,⑹处是System Handler Control and State Register系统处理控制和状态寄存器的地址OS_NVIC_SHCSR、⑺处是Interrupt Control and State Register中断控制和状态寄存器的地址OS_NVIC_INT_CTRL,有关这些寄存器的信息可以访问官网https://developer.arm.com/documentation/ddi0489/f/system-control/register-summary。

STATIC UINT32 OsExcSaveIntStatus(UINT32 type, VOID *arg)
{UINT32 ret;
⑴  UINTPTR excContentEnd = (UINTPTR)MAX_INT_INFO_SIZE + (UINTPTR)g_excContent;(VOID)arg;⑵  *((UINT32 *)g_excContent) = type;g_excContent = (UINT8 *)g_excContent + sizeof(UINT32);⑶  *((UINT32 *)g_excContent) = EXC_INT_STATUS_LEN;g_excContent = (UINT8 *)g_excContent + sizeof(UINT32);/* save IRQ ENABLE reg group */
⑷  ret = memcpy_s(g_excContent, excContentEnd - (UINTPTR)g_excContent,(const VOID *)OS_NVIC_SETENA_BASE, OS_NVIC_INT_ENABLE_SIZE);if (ret != EOK) {return LOS_NOK;}g_excContent = (UINT8 *)g_excContent + OS_NVIC_INT_ENABLE_SIZE;/* save IRQ PEND reg group */ret = memcpy_s(g_excContent, excContentEnd - (UINTPTR)g_excContent,(const VOID *)OS_NVIC_SETPEND_BASE, OS_NVIC_INT_PEND_SIZE);if (ret != EOK) {return LOS_NOK;}g_excContent = (UINT8 *)g_excContent + OS_NVIC_INT_PEND_SIZE;/* save IRQ ACTIVE reg group */ret = memcpy_s(g_excContent, excContentEnd - (UINTPTR)g_excContent,(const VOID *)OS_NVIC_INT_ACT_BASE, OS_NVIC_INT_ACT_SIZE);if (ret != EOK) {return LOS_NOK;}g_excContent = (UINT8 *)g_excContent + OS_NVIC_INT_ACT_SIZE;/* save IRQ Priority reg group */ret = memcpy_s(g_excContent, excContentEnd - (UINTPTR)g_excContent,(const VOID *)OS_NVIC_PRI_BASE, OS_NVIC_INT_PRI_SIZE);g_excContent = (UINT8 *)g_excContent + OS_NVIC_INT_PRI_SIZE;/* save Exception Priority reg group */
⑸  ret = memcpy_s(g_excContent, excContentEnd - (UINTPTR)g_excContent,(const VOID *)OS_NVIC_EXCPRI_BASE, OS_NVIC_EXCPRI_SIZE);if (ret != EOK) {return LOS_NOK;}g_excContent = (UINT8 *)g_excContent + OS_NVIC_EXCPRI_SIZE;/* save IRQ Handler & SHCSR */
⑹  ret = memcpy_s(g_excContent, excContentEnd - (UINTPTR)g_excContent,(const VOID *)OS_NVIC_SHCSR, OS_NVIC_SHCSR_SIZE);if (ret != EOK) {return LOS_NOK;}g_excContent = (UINT8 *)g_excContent + OS_NVIC_SHCSR_SIZE;/* save IRQ Control & ICSR */
⑺  ret = memcpy_s(g_excContent, excContentEnd - (UINTPTR)g_excContent,(const VOID *)OS_NVIC_INT_CTRL, OS_NVIC_INT_CTRL_SIZE);if (ret != EOK) {return LOS_NOK;}g_excContent = (UINT8 *)g_excContent + OS_NVIC_INT_CTRL_SIZE;return LOS_OK;
}

小结

本文介绍了异常信息的转储区域分布情况,介绍异常信息如何初始化,并介绍了两个主要的异常信息转储函数。感谢阅读,如有任何问题、建议,都可以博客下留言给我,谢谢。

点击关注,第一时间了解华为云新鲜技术~

鸿蒙轻内核源码分析:异常钩子模块系统中断异常,如何转储异常信息相关推荐

  1. 鸿蒙轻内核源码分析:虚拟文件系统 VFS

    本文分享自华为云社区<鸿蒙轻内核M核源码分析系列二一 01 虚拟文件系统VFS>,作者:zhushy . VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际 ...

  2. 鸿蒙轻内核源码分析:MMU协处理器

    摘要:本系列首先了解下ARM CP15协处理器的知识,接着介绍下协处理器相关的汇编指令,最后分析下MMU相关汇编代码. 本文分享自华为云社区<鸿蒙轻内核A核源码分析系列六 MMU协处理器> ...

  3. 鸿蒙轻内核源码分析:虚拟内存

    摘要:本文以代码+文字的形式,介绍虚拟内存管理的结构体.相关宏定义,分析内核虚拟地址空间和用户进程虚拟地址空间如何初始化等内容. 本文分享自华为云社区<鸿蒙轻内核A核源码分析系列四(2) 虚拟内 ...

  4. 鸿蒙轻内核源码分析:掌握信号量使用差异

    摘要:本文带领大家一起剖析鸿蒙轻内核的信号量模块的源代码,包含信号量的结构体.信号量池初始化.信号量创建删除.申请释放等. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十一 信号量Semap ...

  5. 鸿蒙轻内核源码分析:文件系统LittleFS

    摘要:本文先介绍下LFS文件系统结构体的结构体和全局变量,然后分析下LFS文件操作接口. 本文分享自华为云社区<# 鸿蒙轻内核M核源码分析系列二一 02 文件系统LittleFS>,作者: ...

  6. 鸿蒙轻内核源码分析:虚实映射

    摘要:本文介绍了MMU虚实映射的基本概念,运行机制,分析了映射初始化.映射查询.映射虚拟内存和物理内存,解除虚实映射,更改映射属性,重新映射等常用接口的代码. 本文分享自华为云社区<使用MRS ...

  7. LiteOS内核源码分析:位操作模块

    摘要:本文带领大家一起剖析了LiteOS位操作模块的源代码,代码非常简单,参考官方示例程序代码,实际编译运行一下,加深理解. 本文分享自华为云社区<LiteOS内核源码分析系列五 LiteOS内 ...

  8. v39.06 鸿蒙内核源码分析(异常接管) | 社会很单纯 复杂的是人 | 百篇博客分析HarmonyOS源码

    子曰:"主忠信,毋友不如己者,过则勿惮改." <论语>:子罕篇 百篇博客系列篇.本篇为: v39.xx 鸿蒙内核源码分析(异常接管篇) | 社会很单纯 复杂的是人 硬件 ...

  9. v74.01 鸿蒙内核源码分析(编码方式篇) | 机器指令是如何编码的 | 百篇博客分析OpenHarmony源码

    Python微信订餐小程序课程视频 https://blog.csdn.net/m0_56069948/article/details/122285951 Python实战量化交易理财系统 https ...

最新文章

  1. hdu 1043 Eight 搜索,哈希
  2. ACE中的Proactor和Reactor
  3. UI常见测试用例-51testing
  4. ECMAScript 6教程 (一)
  5. 将undefault和null的数据转换成bool类型的数据 使用!!
  6. NYOJ2括号配对问题
  7. 前端学习(3049):vue+element今日头条管理-请求获取数据
  8. c语言系统的通用数据结构,(转载)C语言实现通用数据结构的高效设计
  9. python用法查询软件_如何使用Python应用软件实现车牌检测和识别
  10. 如何从 Mac 上的“照片”中导出照片、视频和幻灯片放映?
  11. 服务器宕机可能的原因以及服务器宕机解决办法
  12. 联通光纤猫虚拟服务器设置,联通光纤猫怎么设置 联通光纤猫设置方法【详细步骤】...
  13. 鬼迷心窍 歌词翻译 中译日
  14. LINUX中OFFICE WORD无法输入中文的解决办法
  15. 学了一年php感觉什么都不会,php自学要多久_一个学了1年php后选择放弃
  16. [数论+模板] 分解质因数(模板)
  17. html内容被背景图片遮住怎么办_定义BODY背景图后,DIV的背景图片显示不全(已解决)...
  18. ESP8266入门教程04:连接WIFI热点
  19. Matlab Mobile手机版获取gps数据和加速度信号融合
  20. matlab中pwelch函数计算功率谱密度

热门文章

  1. (23)npm项目发布
  2. Generator函数的基本概念
  3. Git笔记(37) 替换
  4. 视觉SLAM笔记(26) 状态估计问题
  5. Ubuntu Mate 开机自启ROS
  6. 偏振模色散及保偏光纤的正确理解
  7. 单元格宽度_excel单元格如何设成正方形或者1mm的正方形
  8. Luogu P1164小A点菜
  9. 二分图匹配(Luogu3386)
  10. oracle备份表和数据