Zephyr log系统原理之filter

本文说明Zephyr log系统如何执行log过滤显示。

概述 链接到标题

Zephyr-log系统的命令控制章节说明如何通过命令设置不同module在各个backend上的level filter及backend的控制,这里分析是如何实现控制和设置的。log命令控制相关的API在log_cmds.c 。 Zephyr-log系统原理是本文的基础,相关概念和名词直接引用。

Frontend 过滤 链接到标题

主要是在log msg送到log core前和所属module的slot 0进行比较进行首次过滤,slot0保存了module所对应的所有backend内最低等级的level,这里比较可以避免不显示的msg被送到log core,加重不必要的系统负担,详细参考Zephyr-log系统原理log显示章节。

Backend 过滤 链接到标题

go/halt 链接到标题

go/halt是通过设置和判断log_backend_control_block的active标志实现

设置active 链接到标题

使用go/halt控制指定的backend是否显示log msg,代码如下:

log_halt->log_backend_deactivate
static inline void log_backend_deactivate(
				const struct log_backend *const backend)
{
	__ASSERT_NO_MSG(backend != NULL);
	backend->cb->active = false;	//不显示,设置为false
}

log_go->log_backend_activate
static inline void log_backend_activate(const struct log_backend *const backend,
					void *ctx)
{
	__ASSERT_NO_MSG(backend != NULL); //显示,设置为true
	backend->cb->ctx = ctx;
	backend->cb->active = true;	
}

使用active 链接到标题

可以看到设置go/halt就是将backend的log_backend_control_block的active标志改变,继续看active在何处生效:

static inline bool log_backend_is_active(
				const struct log_backend *const backend)
{
	__ASSERT_NO_MSG(backend != NULL);
	return backend->cb->active;
}

static void msg_process(struct log_msg *msg, bool bypass)
{
	struct log_backend const *backend;

	if (!bypass) {
		for (int i = 0; i < log_backend_count_get(); i++) {
			backend = log_backend_get(i);

			if (log_backend_is_active(backend) &&   //这里判断active,为true才会送到backend
			    msg_filter_check(backend, msg)) {
				log_backend_put(backend, msg);
			}
		}
	}

	log_msg_put(msg);
}

disable/enable 链接到标题

enable控制module在指定backend上的log msg level filter,module在指定的backend显示大于设定level的msg. disable会让module在指定backend上不显示任何log信息。 注意disable和halt的不同是,disable只关闭指定module在指定backend上的log显示,而halt会将指定backend上所有的module的log显示全部关闭。参看Zephyr-log系统原理架构章节的示意图。

设置filter 链接到标题

首先注意一点:log level越大,严重等级就越高,其值越小可以参考定义

#define LOG_LEVEL_NONE 0
#define LOG_LEVEL_ERR  1
#define LOG_LEVEL_WRN  2
#define LOG_LEVEL_INF  3
#define LOG_LEVEL_DBG  4

这里看一下代码实现,先看enable

static int log_enable(const struct shell *shell,
		      const struct log_backend *backend,
		      size_t argc,
		      char **argv)
{
	int severity_level;

	severity_level = severity_level_get(argv[1]);   

	if (severity_level < 0) {
		shell_error(shell, "Invalid severity: %s", argv[1]);
		return -ENOEXEC;
	}

	/* Arguments following severity level are interpreted as module names.*/
    //设置module对应backend的level 
	filters_set(shell, backend, argc - 2, &argv[2], severity_level);
	return 0;
}

static void filters_set(const struct shell *shell,
			const struct log_backend *backend,
			size_t argc, char **argv, u32_t level)
{
	int i;
	int id;
	bool all = argc ? false : true;
    //获取要设置module的数量,如果为NULL说明,所有moudle都需要设置
	int cnt = all ? log_sources_count() : argc;

	if (!backend->cb->active) {
		shell_warn(shell, "Backend not active.");
	}

    //设置module上指定backend的filter level
	for (i = 0; i < cnt; i++) {
		id = all ? i : module_id_get(argv[i]);  //设置所有module就依次设置,否则根据设置参数获取指定moudle的id
		if (id >= 0) {
			u32_t set_lvl = log_filter_set(backend,
						       CONFIG_LOG_DOMAIN_ID,
						       id, level);  //设置module上的level

            //设置level如果没生效会进行提示
			if (set_lvl != level) {
				const char *name;

				name = all ?
					log_source_name_get(
						CONFIG_LOG_DOMAIN_ID, i) :
					argv[i];
				shell_warn(shell, "%s: level set to %s.",
					   name, severity_lvls[set_lvl]);
			}
		} else {
			shell_error(shell, "%s: unknown source name.", argv[i]);
		}
	}
}

u32_t log_filter_set(struct log_backend const *const backend,
		     u32_t domain_id,
		     u32_t src_id,
		     u32_t level)
{
	assert(src_id < log_sources_count());

	if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) {
		u32_t new_aggr_filter;

        //获取module的filter,也就是对应slot1~solt9
		u32_t *filters = log_dynamic_filters_get(src_id);

		//获取module所在backend的最大level
		u32_t max = log_filter_get(backend, domain_id,
					   src_id, false);
		//将最大level和要设置的level做比较取较小的(数值越小level越高)
		level = MIN(level, max);

		//设置backend所在slot的level值
		LOG_FILTER_SLOT_SET(filters,
				    log_backend_id_get(backend),
				    level);

		//前文”Frontend 过滤“提过过module所属的slot0是保存module对应的所有backend中最小level
		//因此当module的backend level更新时也要重新更新slot-0的level
		new_aggr_filter = max_filter_get(*filters);

		LOG_FILTER_SLOT_SET(filters,
				    LOG_FILTER_AGGR_SLOT_IDX,
				    new_aggr_filter);
		
	}

	return level;
}

再看一下设置slot level的实现,可以看到就是根据id计算出id所在32bit filter内的位置,然后再将3bit level值写入

#define LOG_LEVEL_BITS 3
#define LOG_FILTER_SLOT_SIZE LOG_LEVEL_BITS
#define LOG_FILTER_SLOT_SHIFT(_id) (LOG_FILTER_SLOT_SIZE * (_id))

#define LOG_FILTER_SLOT_SET(_filters, _id, _filter)		     \
	do {							     \
		*(_filters) &= ~(LOG_FILTER_SLOT_MASK <<	     \
				 LOG_FILTER_SLOT_SHIFT(_id));	     \
		*(_filters) |= ((_filter) & LOG_FILTER_SLOT_MASK) << \
			       LOG_FILTER_SLOT_SHIFT(_id);	     \
	} while (false)

最后看一下disable的实现

static int log_disable(const struct shell *shell,
		       const struct log_backend *backend,
		       size_t argc,
		       char **argv)
{
	//就是将对应的backend level写为none,也就是不让显示
	filters_set(shell, backend, argc - 1, &argv[1], LOG_LEVEL_NONE);
	return 0;
}

以上实现可以总结如下图: filter

  1. 先找到module对应的filter,例如main
  2. 在filter内找到要设置backend的slot,例如uart_backend就是slot-6
  3. 将要新设置的level写入slot-6
  4. 重新计算slot-0的值,在使用了的slot中计算最低level(值最大)

使用filter 链接到标题

代码直接看前面的msg_process,可以看到在msg_filter_check通过后才会送显,我们这里看level是如何被使用的

static bool msg_filter_check(struct log_backend const *backend,
			     struct log_msg *msg)
{
	if (IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) {
		u32_t backend_level;
		u32_t msg_level;

		//获取filter中指定backend的level
		backend_level = log_filter_get(backend,
					       log_msg_domain_id_get(msg),
					       log_msg_source_id_get(msg),
					       true /*enum RUNTIME, COMPILETIME*/);
		//获取要显示msg的level
		msg_level = log_msg_level_get(msg);

		//当msg的level大于backend的level(值小于)送显信息
		return (msg_level <= backend_level);
	} else {
		return true;
	}
}

参考 链接到标题

https://lgl88911.pages.dev/zephyr/zephyr-log%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86/ https://lgl88911.pages.dev/zephyr/zephyr-log%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86%E4%B9%8Bbackend/ https://lgl88911.pages.dev/zephyr/zephyr-log%E7%B3%BB%E7%BB%9F/