Zephyr log系统原理之filter

Creative Commons
本作品采用知识共享署名

本文说明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,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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在何处生效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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越大,严重等级就越高,其值越小可以参考定义

1
2
3
4
5
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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值写入

1
2
3
4
5
6
7
8
9
10
11
#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的实现

1
2
3
4
5
6
7
8
9
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是如何被使用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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.gitee.io/2019/03/10/Zephyr-log%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86/
https://lgl88911.gitee.io/2019/04/03/Zephyr-log%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86%E4%B9%8Bbackend/
https://lgl88911.gitee.io/2019/02/17/Zephyr-log%E7%B3%BB%E7%BB%9F/