写点什么

解读鸿蒙轻内核的监控器:异常钩子函数

发布于: 2021 年 10 月 14 日

摘要:本篇先介绍下支持的异常钩子函数的类型,异常钩子函数的注册、执行等内部操作 API 接口,并介绍下使用异常钩子函数的操作接口。


本文分享自华为云社区《鸿蒙轻内核M核源码分析系列十七(1) 异常钩子函数类型介绍》,作者:zhushy 。

 

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


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

1、异常钩子类型枚举 EXC_TYPE


在文件 utils\los_debug.h 定义异常钩子类型枚举 EXC_TYPE。EXC_REBOOT 用于标记系统重启时的钩子函数,发生重启时调用注册的重启钩子函数;EXC_ASSERT 用于标记断言函数,发生断言时调用注册的断言钩子函数;EXC_STACKOVERFLOW 用于标记任务栈溢出钩子函数,发生任务栈溢出时调用注册的任务栈溢出钩子函数;EXC_INTERRUPT 用于标记中断异常时的钩子函数,发生中断异常时调用注册的中断异常钩子函数。


typedef enum {    EXC_REBOOT,    EXC_ASSERT,    EXC_STACKOVERFLOW,    EXC_INTERRUPT,    EXC_TYPE_END} EXC_TYPE;
复制代码

2、如何注册和执行异常钩子函数


本节我们先看下如何调用和注册异常钩子函数,异常钩子函数的注册和调用的函数 API 定义在 utils\los_debug.c,代码如下。⑴处定义的函数 OsExcHookRegister 用于注册异常钩子函数到全局变量 g_excHook。它的传入的参数 ExcHookFnexcHookFn 是个异常钩子函数,这个钩子函数是定义在文件 components\exchook\los_exchook.c 中的 STATICVOID DoExcHook(EXC_TYPE excType)

后文会详细分析。另外,从代码上可以看出异常钩子函数只有一个,也只能注册一次。⑵处定义的异常钩子执行函数 OsDoExcHook,根据传入的枚举类型 EXC_TYPE 来判断执行什么类型的异常钩子函数。


可以看出这 2 个函数都是内部函数,用函数 OsExcHookRegister 注册的也是全局的异常钩子函数,它实质上对应的其实是个异常钩子函数数组。后文会分析如何通过定义在 components\exchook\los_exchook.c 的 LOS_RegExcHook 函数如何分别注册不同类型的异常钩子函数。下文也会详细分析其他对外函数如何调用 OsDoExcHook 来处理异常。


⑴  VOID OsExcHookRegister(ExcHookFn excHookFn)    {        UINT32 intSave = LOS_IntLock();        if (!g_excHook) {            g_excHook = excHookFn;        }        LOS_IntRestore(intSave);    }
⑵ VOID OsDoExcHook(EXC_TYPE excType) { UINT32 intSave = LOS_IntLock(); if (g_excHook) { g_excHook(excType); } LOS_IntRestore(intSave); }
复制代码

3、使用异常钩子函数的操作


我们从上文知道,注册的全局异常钩子函数只有一个,那就是全局异常钩子函数变量 g_excHook,它根据不同的异常钩子类型来分别处理。我们看下具体如何异常钩子函数的,关于全局异常钩子函数底层的细节后文会详细分析。

3.1 重启


LOS_Reboot 该函数可以在发生系统重启异常时调用,程序僵死在此处等待看门狗 watchdog 等。⑴处根据参数类型 EXC_REBOOT 调用对应的重启异常钩子函数。需要在系统初始化时执行 LOS_RegExcHook(EXC_REBOOT,(ExcHookFn)YourRebootFunction)注册异常钩子函数,才能执行重启异常钩子函数。YourRebootFunction 需要自行定义实现在系统重启异常时执行什么操作。如果没有注册过重启钩子函数则跳过不执行任何操作。


LITE_OS_SEC_TEXT_INIT VOID LOS_Reboot(VOID){⑴  OsDoExcHook(EXC_REBOOT);    HalSysExit();}
复制代码

3.2 断言


LOS_ASSERT 该函数可以用于验证函数的参数合法性,该函数宏定义在文件 utils\los_debug.h。可以看出,如果设置的打印级别数值太低,时不支持断言功能的。如⑴处代码所示,该函数宏需要一个参数 judge。如果参数为假时会执行⑵处的代码,根据参数类型 EXC_ASSERT 调用对应的断言异常钩子函数。需要在系统初始化时执行 LOS_RegExcHook(EXC_ASSERT,(ExcHookFn)YourAssertFunction)注册异常钩子函数,才能执行断言异常钩子函数。YourAssertFunction 需要自行定义实现在断言异常时执行什么操作。如果没有注册过断言钩子函数则跳过不执行任何操作。LOS_ASSERT 后续的⑶处的代码会关闭中断,打印断言错误信息 ASSERTERROR...。


#if PRINT_LEVEL < LOG_ERR_LEVEL#define LOS_ASSERT(judge)#else#define LOS_ASSERT(judge)                                                          \    do {                                                                           \⑴      if ((judge) == 0) {                                                        \⑵          OsDoExcHook(EXC_ASSERT);                                               \⑶          (VOID)LOS_IntLock();                                                   \            PRINT_ERR("ASSERT ERROR! %s, %d, %s\n", __FILE__, __LINE__, __func__); \            while (1) { }                                                          \        }                                                                          \    } while (0)#endif
复制代码

3.3 任务栈溢出


OsDoExcHook(EXC_STACKOVERFLOW)任务栈溢出 OsDoExcHook(EXC_STACKOVERFLOW)被

OsHandleRunTaskStackOverflow 函数和 OsHandleNewTaskStackOverflow 函数调用,这 2 个函数定义在文件 kernel\src\los_task.c,分别在当前运行任务,要调度运行的新任务发生任务栈溢出时调用。当执行到⑴、⑵处的代码时,根据参数类型 EXC_STACKOVERFLOW 调用对应的异常钩子函数。需要在系统初始化时执行 LOS_RegExcHook(EXC_STACKOVERFLOW,(ExcHookFn)YourStackOverflowFunction)注册异常钩子函数,才能执行异常钩子函数。YourStackOverflowFunction 需要自行定义实现在任务栈溢出异常时执行什么操作。如果没有注册过钩子函数则跳过不执行任何操作。


LITE_OS_SEC_TEXT STATIC VOID OsHandleRunTaskStackOverflow(VOID){    PRINT_ERR("CURRENT task ID: %s:%d stack overflow!\n",              g_losTask.runTask->taskName, g_losTask.runTask->taskID);⑴  OsDoExcHook(EXC_STACKOVERFLOW);}......LITE_OS_SEC_TEXT STATIC VOID OsHandleNewTaskStackOverflow(VOID){    ......    tmp = g_losTask.runTask;    g_losTask.runTask = g_losTask.newTask;⑵  OsDoExcHook(EXC_STACKOVERFLOW);    g_losTask.runTask = tmp;}
复制代码

3.4 中断异常


HalExcHandleEntry 该函数在发生中断异常时汇编代码中调用执行,用于处于系统异常,该函数宏定义在不同芯片架构实现的文件 los_interrupt.c 中,如 kernel\arch\arm\cortex-m7\gcc\los_interrupt.c。处理系统中断异常时,执行到⑴处代码时,会根据参数类型 EXC_INTERRUPT 调用对应的异常钩子函数。和上述几个异常类型的钩子函数不一样,中断异常钩子函数不需要用户来注册,内核已经注册了中断异常钩子函数。相应的代码在文件 components\exchook\los_exc_info.c 中,注册代码语句为(VOID)LOS_RegExcHook(EXC_INTERRUPT,(ExcHookFn)OsExcMsgDump);,当发生系统中断异常时会调用(ExcHookFn)OsExcMsgDump 函数,后文会详细分析都包含哪些异常信息。


LITE_OS_SEC_TEXT_INIT VOID HalExcHandleEntry(UINT32 excType, UINT32 faultAddr, UINT32 pid, EXC_CONTEXT_S *excBufAddr){    ......⑴  OsDoExcHook(EXC_INTERRUPT);    OsExcInfoDisplay(&g_excInfo);    HalSysExit();}
复制代码

小结


本文介绍了异常钩子函数的注册函数 OsExcHookRegister 和异常钩子函数的调用函数 OsDoExcHook,以及介绍了支持的异常钩子函数类型等。


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

发布于: 2021 年 10 月 14 日阅读数: 19
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
解读鸿蒙轻内核的监控器:异常钩子函数