在线时间4069 小时
UID3441752
注册时间2017-11-21
NXP金币753013
TA的每日心情 | 开心 2024-3-26 15:16 |
---|
签到天数: 266 天 [LV.8]以坛为家I
管理员
- 积分
- 32030
- 最后登录
- 2024-4-26
|
清风徐来——Zephyr实战篇(7)之log系统
在正式开篇之前,先抛出一个问题,大家认为对于系统开发而言什么是最重要的?小编认为是调试信息,因为调试信息可以让我们了解当前系统中程序的运行状态,并将其显示出来,让我们能够按图索骥,尤其在出现问题时,能够精准定位。
那么本期,小编要和大家聊聊Zephyr中的log系统,没错,Zephyr中包含了一个非常强大的log系统。一般log的输出往往会占用一定的CPU时间,从而对程序逻辑产生影响,导致不可预期的后果。
而在Zephyr中,依赖于log缓冲区的存在,几乎不会占用当前程序上下文执行时间,log信息会被首先缓存到缓存区,然后当系统空闲时,这些数据才会被输出到后端通信设备(例如串口,网络等)。
下面我们来具体看下ZephyrOS中log系统的一些特点。
异步显示能力,不占用当前上下文时间
模块级log信息管理
同模块中多实例
同时支持编译时和运行时的log信息过滤
信息时间戳
多显示后端支持,目前共支持9种不同的后端:
Log系统的组成 Zephyr的log系统由三部分构成,分别是:
1. 前端API:主要用于modules/system中,包括但不限于:
log模块的注册和声明
log级别设置
不同类型信息显示,包括error,warning,info, debug messages以及hexdump
模块不同实例定义与注册
2. log核心:负责管理已经注册的log模块和实例化对象,将log信息分发到不同的后端以进行显示
3. 后端:log显示终端,提供不同的log信息显示策略
如图所示,为Zephyr的log系统的一个总体架构示意图,首先通过前端API进行log模块的注册和初始化,添加log信息,随后log core将这些信息分发到不同的显示后端中进行显示:
Log系统的管理 我们的log系统如何进行管理呢?当然了,既然是在ZephyrOS中,一定是通过Kconfig选项进行控制了,其中包括:
1. 过滤支持(subsys/logging/Kconfig.filtering):
CONFIG_LOG_RUNTIME_FILTERING : 支持运行期动态调整
CONFIG_LOG_DEFAULT_LEVEL : 默认系统log级别(0-4)
CONFIG_LOG_OVERRIDE_LEVEL : 全局配置最小log级别,针对所有注册的log模块
CONFIG_LOG_MAX_LEVEL : 全局配置最大log级别,针对所有注册的log模块
2. log打印模式(subsys/logging/Kconfig.mode):
CONFIG_LOG_MODE_DEFERRED : log信息会被缓存,稍后处理
CONFIG_LOG_MODE_IMMEDIATE : log信息会立刻被处理,当然,会影响当前上下文代码的执行
CONFIG_LOG_MODE_MINIMAL : log系统的最小实现
3. 处理过程(subsys/logging/Kconfig.processing)
CONFIG_LOG_PRINTK : log信息直接被打印
CONFIG_LOG_MODE_OVERFLOW : 当log缓存满了之后,丢掉老的log信息
CONFIG_LOG_BLOCK_ IN_THREAD : 当log缓存满了之后,log系统会被阻塞
4. 格式化(subsys/logging/Kconfig.formatting)
CONFIG_LOG_IMMEDIATE_CLEAN_OUTPUT : 在log信息处理时插入中断锁,保证处理过程不会被打断
CONFIG_LOG_BACKEND_SHOW_COLOR : 红色代表error,红色代表warning
Log模块的定义 那么如何定义一个log模块呢?很简单,每一个模块子系统只需要调用LOG_MODULE_REGISTER()即可完成log模块的声明,并将其注册到log系统中。
但是,这里有个需要注意的地方,如果一个模块包含多个文件,这些额外的文件需要使用LOG_MODULE_DECLARE()来声明。看一个简单的例子:
- #include <zephyr.h>
- #include <logging/log.h>
- LOG_MODULE_REGISTER(MODULE_NAME, CONFIG_MY_MODULE_LOG_LEVEL);
- const char *sample_module_name_get(void)
- {
- return STRINGIFY(MODULE_NAME);
- }
- void sample_module_func(void)
- {
- LOG_INF("log in test_module %d", 11);
- }
复制代码 这里的CONFIG_MY_MODULE_LOG_LEVEL需要通过kconfig文件进行定义,而如果我们不显式定义这个LEVEL的话,比如我们可以使用LOG_MODULE_REGISTER(MODULE_NAME)来完成同样的log模块的注册,只不过会使用缺省的CONFIG_LOG_DEFAULT_LEVEL作为log模块级别。
Log模块的级别定义
Zephyr的log系统定义了5个级别,分别是:
·LOG_LEVEL_NONE : 0U
·LOG_LEVEL_ERR : 1U
·LOG_LEVEL_WRN : 2U
·LOG_LEVEL_INF : 3U
·LOG_LEVEL_DBG : 4U
同样的,Zephyr提供了对应级别的log打印函数:
·LOG_ERR(…), LOG_HEXDUMP_ERR(_data, _length, _str)
·LOG_WRN(…), LOG_HEXDUMP_WRN(_data, _length, _str)
·LOG_INF(…), LOG_HEXDUMP_WRN(_data, _length, _str)
·LOG_DBG(…), LOG_HEXDUMP_WRN(_data, _length, _str)
这样一来,我们就可以直接调用相应的宏,进行不同级别的log信息打印。
当然,如果我们在定义log模块时候,定义了模块log级别为LOG_LEVEL_NONE,相当于禁止了log功能,不会有任何log信息打印出来,这样一来也就实现了编译期模块级的log级别过滤。
Log信息的过滤 那么怎么实现运行期间的log信息过滤呢?
先需要定义CONFIG_LOG_RUNTIME_FILTERING=y使能运行期过滤。但是,需要注意,这样一来会提高RAM/FLASH的使用量,因此在使用时,需要权衡考虑。
同时,为了实现运行期控制,需要使能shell,log模块的定义方法与静态方法一致,只不过,我们现在可以在控制台通过shell指令进行动态控制,假设我们通过LOG_MODULE_REGISTER(myapp)定义了一个叫myapp的模块,那么我们就可以:
·log enable inf myapp
·log disable
·log enable err
·log list_backends
·log status
其中,log status是一条很有用的指令,可以列出当前注册到log系统中的所有log模块,包括他们当前的过滤级别以及他们的固有(编译期指定)的过滤级别。
Log系统对性能的影响 最后我们来看下,log系统的使能对系统的影响。
首先毋庸置疑的是,log系统的添加提供了很多有用的调试信息,但是万物都有两面性:
·ram/flash的额外占用
·时序影响(尤其是当log打印与程序的执行在同一个上下文中时)
下面是不同的配置对系统ram/flash占用的影响,差别还是很明显的:
好了,今天小编就给大家介绍到这儿了,主要和大家分享了Zephyr中的log系统,以及如何创建一个我们自己的log模块并通过shell动态设置log显示级别,以实现运行期的log信息过滤。我们下期见。
|
|