鸿蒙轻内核M核源码分析系列十七(3) 异常信息ExcInfo

本文中所涉及的源码,以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_BASEInterrupt Active Bit Register中断活跃寄存器的地址OS_NVIC_INT_ACT_BASEInterrupt 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;
}

小结

本文介绍了异常信息的转储区域分布情况,介绍异常信息如何初始化,并介绍了两个主要的异常信息转储函数。

如果大家想更加深入的学习 OpenHarmony 开发的内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……

系统架构分析:https://qr18.cn/CgxrRy

  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/24232.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[DT] 翻译笔记

这本书由129位作者贡献了50章。这一章是本书每一章的一般介绍。从第二章开始&#xff0c;介绍了数字孪生的概念、架构描述、设计规范和应用场景。第2节介绍了数字孪生的概念和发展。第3节介绍了促进数字孪生发展的核心技术。第4节介绍了数字孪生的一般框架和构建方法。第5节介绍…

PHP小方法

一、随机生成姓名 二、随机获取身份证 三、随机获取手机号 四、随机获取省 五、通过身份证获取生日和性别 六、通过身份证获取年龄 七、获取访问IP 八、获取访问URL地址 九、陆续增加 //一、随机生成姓名 function generateName(){$arrXing getXingList();$numbXing …

C++中的lambda表达式详解

Lambda表达式是一种可以在代码中定义匿名函数的方式&#xff0c;通常用于简化代码&#xff0c;使其更紧凑和易读。它的语法如下&#xff1a; [capture](parameters) -> return_type { body }下面逐个解释每个部分&#xff1a; 1. 捕获列表 (capture) 捕获列表用于指定哪些…

计算机组成实验---Cache的实现

直接映射 先看懂cache的映射原理&#xff0c;根据cache大小与主存大小来计算各个信号线的位数 各个信号线位数 主存地址在逻辑上分为区号、块号、块内地址 Cache结构 Cache访问原理 基本过程 状态机&#xff1a;“三段式”实现 6.3 Verilog 状态机 | 菜鸟教程 (runoob.com) …

算法:226. 翻转二叉树

给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#xff1a;[2,3,1]示例 3&#x…

03-3.2.3 队列的链式存储的实现

&#x1f44b; Hi, I’m Beast Cheng&#x1f440; I’m interested in photography, hiking, landscape…&#x1f331; I’m currently learning python, javascript, kotlin…&#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以订…

服务监控-微服务小白入门(5)

背景 什么是服务监控 监视当前系统应用状态、内存、线程、堆栈、日志等等相关信息&#xff0c;主要目的在服务出现问题或者快要出现问题时能够准确快速地发现以减小影响范围。 为什么要使用服务监控 服务监控在微服务改造过程中的重要性不言而喻&#xff0c;没有强大的监控…

draw.io 如何设置图形圆角?

draw.io 如何设置图形圆角呢&#xff1f; draw.io 是一款强大的&#xff0c;免费的开源工具&#xff0c;我经常用它来画流程图&#xff0c;但是我发现 draw.io 对于图形圆角的设置&#xff0c;只提供了一个设置选项&#xff0c;如下图&#xff1a; 当你选中某个图形&#xff0…

啵啵啵啵啵啵啵啵啵啵啵啵啵啵啵

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

通过U盘将第三方软件安装到各大品牌电视的方法

在本教程中&#xff0c;小武给大家整理了通过U盘的方式安装第三方软件到电视盒子上&#xff0c;可直接使用通用U盘的方式来进行安装。 如果您相应电视品牌按通用方式无法完成需求&#xff0c;下面为您也贴心整理了20款主流智能电视和电视盒子的U盘安装指南。这些步骤适用于小米…

docker与docker-compose安装

1.1 安装工具 sudo yum install -y yum-utils device-mapper-persistent-data lvm21.2 添加docker的yum库 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sudo yum makecache fast1.3 安装Docker sudo yum install docke…

GPT-4o:突出优势 和 应用场景

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

2024世界人工智能大会青年优秀论文TOP20发布

作为 WAIC 2024 针对青年科学家的重要奖项&#xff0c;该奖项关注人工智能基础理论、数学基础、机器学习、计算机视觉与模式识别、自然语言处理等前沿领域。 2024 年 3 月&#xff0c;《关于推荐“2024 世界人工智能大会青年优秀论文奖”参评论文的通知》发布&#xff0c;面向…

window安装苹果虚拟机

vmware workstation pro 17.5 安装 macos 13.5.2 虚拟机超详细图文教程_vmware workstation 17.5.0 pro unlocker & oem bios -CSDN博客

【JVM】从编译后的指令集来再次理解++i和i++的执行顺序

JVM为什么要选用基于栈的指令集架构 与基于寄存器的指令集架构相比&#xff0c;基于栈的指令集架构不依赖于硬件&#xff0c;因此可移植性更好&#xff0c;跨平台性更好因为栈结构的特性&#xff0c;永远都是先处理栈顶的第一条指令&#xff0c;因此大部分指令都是零地址指令&…

利用keepalived对zabbix-server做高可用,部署安装keepalived

有2台机器&#xff0c;每台都有1个zabbix-server&#xff0c;然后再每台上再装一个keepalived https://www.keepalived.org/download.html 1&#xff0c;创建安装路径 mkdir /usr/share/keepalived/2&#xff0c;在这个安装路径下面下载keepalived的软件包 我选的版本是1.3…

Redis的事务与关系型数据库事务有何不同?

引言&#xff1a;关于 Redis 的事务很多人可能都是一知半解&#xff0c;大多数人只了解数据库的事务&#xff0c;并且是单体事务&#xff0c;对于 Redis 事务和常见关系型数据库的事务的区别还没有去了解过&#xff0c;本文就来详细进行介绍。 题目 Redis的事务与关系型数据库…

Go微服务: 基于使用场景理解分布式之二阶段提交

概述 二阶段提交&#xff08;Two-Phase Commit&#xff0c;2PC&#xff09;是一种分布式事务协议&#xff0c;用于在分布式系统中确保多个参与者的操作具有原子性即所有参与者要么全部提交事务&#xff0c;要么全部回滚事务&#xff0c;以维持数据的一致性它分为两个阶段进行&…

Towards Graph Contrastive Learning: A Survey and Beyond

目录 Towards Graph Contrastive Learning- A Survey and Beyond摘要IntroductionPRELIMINARY符号说明GNN对比学习下游任务 GCL自监督学习增强策略基于规则随机扰动或mask子图采样图扩散 基于学习图结构学习图对抗训练图合理化 对比模式同尺度对比全局上下文局部 跨尺度对比局部…

Polar Web【中等】写shell

Polar Web【中等】写shell Contents Polar Web【中等】写shell思路&探索EXP运行&总结 思路&探索 初看题目&#xff0c;预测需要对站点写入木马&#xff0c;具体操作需要在过程中逐步实现。 打开站点(见下图)&#xff0c;出现 file_put_contents 函数&#xff0c;其…