最近从STM32换成NXP的S32K1平台做项目,从读手册调外设驱动开始,关于外设驱动是怎么调的,我用的软件是S32 Design Studio for ARM Version 2.2 在官网下载的,注册账号产品码会发到邮箱选择ONLINE验证,安装完成就可以调试驱动了。
回归主题!
这次想说的是嵌入式平台打印log的函数分析,打印log开源项目都有很多比如Zlog,easylogger等,这次分析一下easylogger中部分源码的作用。
我的C语言水平一般,看起来还挺吃力的
#define elog_info(tag, ...) \
elog_output(ELOG_LVL_INFO, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
//我们的主角elog_output
#define elog_i(tag, ...) elog_info(tag, __VA_ARGS__)
#define log_i(...) elog_i(LOG_TAG, __VA_ARGS__)
//解释一下__VA_ARGS__:用于在宏替换部分中,表示可变参数列表;
[我的参考] (https://blog.csdn.net/auccy/article/details/88833659)
log_a("Hello EasyLogger!");
log_e("Hello EasyLogger!");
log_w("Hello EasyLogger!");
log_i("Hello EasyLogger!");
log_d("Hello EasyLogger!");
log_v("Hello EasyLogger!");
分析一下elog_output,我不会的大家有想法的都可以留言一起进步!
他是一个变长参数函数前三个
void elog_output(uint8_t level, const char *tag, const char *file, const char *func,
const long line, const char *format, ...) {
extern const char *elog_port_get_time(void);
extern const char *elog_port_get_p_info(void);
extern const char *elog_port_get_t_info(void);
//elog提供了三个接口三个指针函数返回值用来获取当前时间、获取进程号、获取线程号
//因为本文中移植到裸机工程中,并且没有提供时间支持
//所以这三个接口都返回空字符串
size_t tag_len = strlen(tag), log_len = 0, newline_len = strlen(ELOG_NEWLINE_SIGN);
//获得tag的长度,初始化局部LOG的长度
//#define ELOG_NEWLINE_SIGN "\n" 新一行标志的长度
//输出行号最大长度
//#define ELOG_LINE_NUM_MAX_LEN 5
//#define ELOG_FILTER_TAG_MAX_LEN 30
char line_num[ELOG_LINE_NUM_MAX_LEN + 1] = { 0 };
char tag_sapce[ELOG_FILTER_TAG_MAX_LEN / 2 + 1] = { 0 };
//
va_list args;
//首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
//然后用VA_START宏初始化变量刚定义的VA_LIST变量;
int fmt_result;
//EasyLogger自带的断言,可以直接用户软件,在断言表达式不成立后会输出断言信息并保持while(1),
//或者执行断言钩子方法,钩子方法的设定参考 elog_assert_set_hook
/*关于断言
对于断言,相信大家都不陌生,大多数编程语言也都有断言这一特性。简单地讲,断言就是对某种假设条件进行检查。在 C 语言中,断言被定义为宏的形式(assert(expression)),而不是函数,其原型定义在<assert.h>文件中。其中,assert 将通过检查表达式 expression 的值来决定是否需要终止执行程序。也就是说,如果表达式 expression 的值为假(即为 0),那么它将首先向标准错误流 stderr 打印一条出错信息,然后再通过调用 abort 函数终止程序运行;否则,assert 无任何作用。
*/
ELOG_ASSERT(level <= ELOG_LVL_VERBOSE);
//elog_init中初始化elog.output_enabled
/* check output enabled */
if (!elog.output_enabled) {
return;
}
//过滤器等级 elog_set_filter_lvl(ELOG_LVL_VERBOSE);elog_init初始化
/* level filter */
if (level > elog.filter.level || level > elog_get_filter_tag_lvl(tag)) {
return;
} else if (!strstr(tag, elog.filter.tag)) { /* tag filter */
return;
}
//获得并指向第一个参数
/* args point to the first variable parameter */
va_start(args, format);
/* lock output 这里边实现是关中断可能是为了保证日志的正确性用关闭全局中断来加锁*/
elog_output_lock();
#ifdef ELOG_COLOR_ENABLE
/* add CSI start sign and color info */
if (elog.text_color_enabled) {
log_len += elog_strcpy(log_len, log_buf + log_len, CSI_START);
log_len += elog_strcpy(log_len, log_buf + log_len, color_output_info[level]);
}
#endif
//elog_strcpy的实现参考strcpy实现
//即把从src地址开始的字符串复制到以dest开始的地址空间
/* package level info */
if (get_fmt_enabled(level, ELOG_FMT_LVL)) {
log_len += elog_strcpy(log_len, log_buf + log_len, level_output_info[level]);
}
/* package tag info */
if (get_fmt_enabled(level, ELOG_FMT_TAG)) {
log_len += elog_strcpy(log_len, log_buf + log_len, tag);
/* if the tag length is less than 50% ELOG_FILTER_TAG_MAX_LEN, then fill space */
if (tag_len <= ELOG_FILTER_TAG_MAX_LEN / 2) {
memset(tag_sapce, ' ', ELOG_FILTER_TAG_MAX_LEN / 2 - tag_len);
log_len += elog_strcpy(log_len, log_buf + log_len, tag_sapce);
}
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
/* package time, process and thread info */
if (get_fmt_enabled(level, ELOG_FMT_TIME | ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) {
log_len += elog_strcpy(log_len, log_buf + log_len, "[");
/* package time info */
if (get_fmt_enabled(level, ELOG_FMT_TIME)) {
log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_time());
if (get_fmt_enabled(level, ELOG_FMT_P_INFO | ELOG_FMT_T_INFO)) {
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
}
/* package process info */
if (get_fmt_enabled(level, ELOG_FMT_P_INFO)) {
log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_p_info());
if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) {
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
}
/* package thread info */
if (get_fmt_enabled(level, ELOG_FMT_T_INFO)) {
log_len += elog_strcpy(log_len, log_buf + log_len, elog_port_get_t_info());
}
log_len += elog_strcpy(log_len, log_buf + log_len, "] ");
}
/* package file directory and name, function name and line number info */
if (get_fmt_enabled(level, ELOG_FMT_DIR | ELOG_FMT_FUNC | ELOG_FMT_LINE)) {
log_len += elog_strcpy(log_len, log_buf + log_len, "(");
/* package file info */
if (get_fmt_enabled(level, ELOG_FMT_DIR)) {
log_len += elog_strcpy(log_len, log_buf + log_len, file);
if (get_fmt_enabled(level, ELOG_FMT_FUNC)) {
log_len += elog_strcpy(log_len, log_buf + log_len, ":");
} else if (get_fmt_enabled(level, ELOG_FMT_LINE)) {
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
}
/* package line info */
if (get_fmt_enabled(level, ELOG_FMT_LINE)) {
snprintf(line_num, ELOG_LINE_NUM_MAX_LEN, "%ld", line);
log_len += elog_strcpy(log_len, log_buf + log_len, line_num);
if (get_fmt_enabled(level, ELOG_FMT_FUNC)) {
log_len += elog_strcpy(log_len, log_buf + log_len, " ");
}
}
/* package func info */
if (get_fmt_enabled(level, ELOG_FMT_FUNC)) {
log_len += elog_strcpy(log_len, log_buf + log_len, func);
}
log_len += elog_strcpy(log_len, log_buf + log_len, ")");
}
/* package other log data to buffer. '\0' must be added in the end by vsnprintf. */
//将可变参数格式化输出到一个字符数组
//参数1 生成的格式化的字符串存放
//参数2可接受的最大字符数
//参数3 指定输出格式
//返回值:执行成功,返回最终生成字符串的长度
fmt_result = vsnprintf(log_buf + log_len, ELOG_LINE_BUF_SIZE - log_len, format, args);
//用va_end宏结束可变参数的获取
va_end(args);
/* calculate log length */
if ((log_len + fmt_result <= ELOG_LINE_BUF_SIZE) && (fmt_result > -1)) {
log_len += fmt_result;
} else {
/* using max length */
log_len = ELOG_LINE_BUF_SIZE;
}
/* overflow check and reserve some space for CSI end sign and newline sign */
#ifdef ELOG_COLOR_ENABLE
if (log_len + (sizeof(CSI_END) - 1) + newline_len > ELOG_LINE_BUF_SIZE) {
/* using max length */
log_len = ELOG_LINE_BUF_SIZE;
/* reserve some space for CSI end sign */
log_len -= (sizeof(CSI_END) - 1);
#else
if (log_len + newline_len > ELOG_LINE_BUF_SIZE) {
/* using max length */
log_len = ELOG_LINE_BUF_SIZE;
#endif /* ELOG_COLOR_ENABLE */
/* reserve some space for newline sign */
log_len -= newline_len;
}
//关键字截取 符串里的log_buf含有keyword字符串的情况
/* keyword filter */
if (elog.filter.keyword[0] != '\0') {
/* add string end sign */
log_buf[log_len] = '\0';
/* find the keyword */
if (!strstr(log_buf, elog.filter.keyword)) {
/* unlock output */
elog_output_unlock();//取消关闭中断
return;
}
}
//因为我是做汽车电子的项目这里临界区对于日志的保护是否有必要?
//各位可以畅所欲言
#ifdef ELOG_COLOR_ENABLE
/* add CSI end sign */
if (elog.text_color_enabled) {
log_len += elog_strcpy(log_len, log_buf + log_len, CSI_END);
}
#endif
/* package newline sign */
log_len += elog_strcpy(log_len, log_buf + log_len, ELOG_NEWLINE_SIGN);
//加入换行标志\n
/* output log */
#if defined(ELOG_ASYNC_OUTPUT_ENABLE)
extern void elog_async_output(uint8_t level, const char *log, size_t size);
elog_async_output(level, log_buf, log_len);
#elif defined(ELOG_BUF_OUTPUT_ENABLE)
extern void elog_buf_output(const char *log, size_t size);
elog_buf_output(log_buf, log_len);
#else
elog_port_output(log_buf, log_len);
#endif
//输出接口
/* unlock output */
elog_output_unlock();
}
剩下的功能需要在移植之后跑一下来验证!