本篇文章从四个方面对dpdk日志系统进行剖析,分别是:
1、dpdk日志框架
2、内存池未初始化时的日志系统
3、内存池已完成初始化的日志系统
4、日志系统的使用
下面分别对这四个方面进行分析:
一、dpdk日志框架
在分析dpdk源码时,难免存在有疑惑的地方,这个时候可以添加调试日志来消除的疑问。dpdk通过fopencookie接口自己实现了一个日志系统,下面我们来看下这个fopencookie是如何使用的。
fopencookie会让我们注册四个接口,分别是读文件、写文件、定位文件的偏移、关闭文件接口。上图这个console_log_func结构,里面就包含了注册的四个接口。 那这些注册的接口是什么时候被调用呢。 通常情况下在进行系统调用情况下会调用这些接口。例如调用write函数写文件时,会调用到console_log_write接口; 在调用read函数读取文件内容时,会调用到console_log_read接口。
gdb调试这个进程,可以验证上面这个调用的过程。rte_log开始写日志时,会进行write的系统调用,最终调用到console_log_write接口
二、内存池未初始化时的日志系统
在进程刚启动的时候,从main函数开始到rte_eal_log_early_init调用前,如果需要打印些日志信息,只能够使用printf打印日志到屏幕, 而不能使用RTE_LOG函数来打印日志,否则进程将崩溃。
只有rte_eal_log_early_init调用后,才能使用RTE_LOG来打印日志信息。这个时候由于内存池还没有初始化操作,是没有将日志缓存到内存中, 此时日志是实时打印到屏幕上的。从rte_eal_log_early_init接口到rte_eal_log_init之间,所有的RTE_LOG打日志操作,都是将日志立即打印到屏幕上,而不经过内存池处理。看下堆栈信息就能够验证这个问题,这个时候的打日志是通过ealy_log_write直接打印到屏幕上。
三、内存池已完成初始化的日志系统
上面已经说了,从rte_eal_log_early_init接口到rte_eal_log_init之间,所有的RTE_LOG打日志操作,都是将日志立即打印到屏幕上,而不经过内存池处理。那rte_eal_log_init初始化之后呢? 在rte_eal_log_early_init接口与rte_eal_log_init之间,会进行内存池的初始化操作, 内存池初始化之后,以及rte_eal_log_init函数调用后,接下里也是通RTE_LOG来打印日志。日志除了打印到屏幕外,还会保存到内存中,然后定期将内存中的日志信息写入到文件保存(dpdk目前只提供了保存到文件的接口,通常在实际开发过程中由应用进程自己定期保存日志到文件)。从堆栈中可以看出,rte_eal_log_init初始化之后,打印日志是通过rte_log_add_in_history接口,而不再是ealy_log_write接口, 将日志信息保存到链表中,链表节点则是从内存池中获取的。
四、日志系统的使用
在rte_eal_log_early_init函数初始化完成之后,可以通过RTE_LOG来打印日志。 可以指定要打印的模块,日志级别。例如打印table模块的日志,日志级别为ERR; 打印pipeline模块的日志,日志级别为info; 打印eal模块的日志,日志级别为debug
RTE_LOG(ERR, PMD, "hello! this is pmd module");
RTE_LOG(INFO, PIPELINE, "hello! this is pipeline module");
RTE_LOG(DEBUG, EAL, "hello! this is eal module");
那dpdk支持打印哪些模块的日志呢?这个可以从rte_log.h宏定义可以看出,例如:
支持的日志类型
#define RTE_LOGTYPE_EAL 0x00000001
#define RTE_LOGTYPE_MALLOC 0x00000002
#define RTE_LOGTYPE_RING 0x00000004
每个模块又可以支持哪些日志级别呢?也是在rte_log.h中宏定义,例如:
支持的日志级别
#define RTE_LOG_ERR 4U /**< Error conditions. */
#define RTE_LOG_WARNING 5U /**< Warning conditions. */
#define RTE_LOG_NOTICE 6U /**< Normal but significant condition. */
#define RTE_LOG_INFO 7U /**< Informational. */
#define RTE_LOG_DEBUG 8U /**< Debug-level messages. */
从上面可以看出,dpdk支持很多模块的日志。那是不是只要随便调用RTE_LOG就能打印出日志呢? 答案肯定不行,dpdk自己实现了日志过滤器。只有用户调用rte_set_log_type设置了需要打印哪些模块,则这些模块才能打印出来,其他模块则过滤掉了,无法打印出来。例如调用rte_set_log_type设置了EAL, MALLOC两个模块,则这两个模块可以打印出日志,但RTE_LOG(DEBUG, PIPELINE, "hello pipeline")是无法打印出来了,因为没有设置PIPELINE这个模块。
同样的,日志级别也是如此,通过rte_set_log_level来设置日志级别,小于这个日志级别的日志都可以打印出来。例如调用rte_set_log_level设置日志级别为RTE_LOG_CRIT,则小于ERR的日志级别:RTE_LOG_EMERG;RTE_LOG_ALERT;RTE_LOG_CRIT都可以打印出来,其他的日志级别就不行。