1 /*
2 * Copyright (c) 2022 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/ztest.h>
8 #include <zephyr/sys/util.h>
9 #include <string.h>
10 #include <zephyr/ztress.h>
11 #include <zephyr/random/random.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/logging/log_ctrl.h>
14 #include <zephyr/logging/log_backend.h>
15
16 #define MODULE_NAME test
17
18 LOG_MODULE_REGISTER(MODULE_NAME);
19
20 static uint32_t test_source_id;
21
22 #define CNT_BITS 28
23
24 struct mock_log_backend {
25 uint32_t last_id[16];
26 uint32_t cnt[16];
27 uint32_t dropped;
28 uint32_t missing;
29 };
30
31 static struct mock_log_backend mock_backend;
32 static uint32_t log_process_delay = 10;
33
handle_msg(uint32_t arg0)34 static void handle_msg(uint32_t arg0)
35 {
36 uint32_t ctx_id = arg0 >> CNT_BITS;
37 uint32_t id = arg0 & BIT_MASK(CNT_BITS);
38 int off = id - mock_backend.last_id[ctx_id] - 1;
39
40 mock_backend.cnt[ctx_id]++;
41
42 k_busy_wait(log_process_delay);
43
44 if (off > 0) {
45 mock_backend.missing += off;
46 }
47
48 mock_backend.last_id[ctx_id] = id;
49 }
50
process(const struct log_backend * const backend,union log_msg_generic * msg)51 static void process(const struct log_backend *const backend,
52 union log_msg_generic *msg)
53 {
54 size_t len;
55 uint8_t *package = log_msg_get_package(&msg->log, &len);
56
57 package += 2 * sizeof(void *);
58
59 handle_msg(*(uint32_t *)package);
60 }
61
mock_init(struct log_backend const * const backend)62 static void mock_init(struct log_backend const *const backend)
63 {
64
65 }
66
panic(struct log_backend const * const backend)67 static void panic(struct log_backend const *const backend)
68 {
69 zassert_true(false);
70 }
71
dropped(const struct log_backend * const backend,uint32_t cnt)72 static void dropped(const struct log_backend *const backend, uint32_t cnt)
73 {
74
75 mock_backend.dropped += cnt;
76 }
77
78 static const struct log_backend_api log_backend_api = {
79 .process = process,
80 .panic = panic,
81 .init = mock_init,
82 .dropped = IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) ? dropped : NULL,
83 };
84
85 LOG_BACKEND_DEFINE(test, log_backend_api, true, NULL);
86
validate(int ctx_cnt)87 static void validate(int ctx_cnt)
88 {
89 uint64_t in_cnt = 0;
90 uint64_t out_cnt = 0;
91
92 for (int i = 0; i < ctx_cnt; i++) {
93 /* First exectution skips (-1) but there is one final round of logging
94 * when ztress execution is completed (+1).
95 */
96 in_cnt += ztress_exec_count(i) - 1 + 1;
97 out_cnt += mock_backend.cnt[i];
98 }
99
100 out_cnt += mock_backend.dropped;
101
102 zassert_equal(mock_backend.dropped, mock_backend.missing,
103 "dropped:%u missing:%u",
104 mock_backend.dropped, mock_backend.missing);
105 zassert_equal(in_cnt, out_cnt);
106 }
107
context_handler(void * user_data,uint32_t cnt,bool last,int prio)108 static bool context_handler(void *user_data, uint32_t cnt, bool last, int prio)
109 {
110 /* Skip first execution to start from id = 1. That simplifies handling in
111 * the backend and validation of message.
112 */
113 if (cnt == 0) {
114 return true;
115 }
116
117 uint32_t i = cnt | (prio << CNT_BITS);
118
119 switch (sys_rand8_get() % 4) {
120 case 0:
121 LOG_INF("%u", i);
122 break;
123 case 1:
124 LOG_INF("%u %u %u %u", i, 1, 2, 3);
125 break;
126 case 2:
127 {
128 char test_str[] = "test string";
129 LOG_INF("%u %s %s", i, "test", test_str);
130 break;
131 }
132 default:
133 LOG_INF("%u %d", i, 100);
134 break;
135 }
136
137 return true;
138 }
139
get_test_timeout(void)140 static int get_test_timeout(void)
141 {
142 if (IS_ENABLED(CONFIG_BOARD_QEMU_CORTEX_A9)) {
143 /* Emulation for that target is extremely slow. */
144 return 500;
145 }
146
147 if (IS_ENABLED(CONFIG_BOARD_QEMU_X86)) {
148 /* Emulation for that target is very fast. */
149 return 10000;
150 }
151
152 if (IS_ENABLED(CONFIG_BOARD_QEMU_X86_64)) {
153 /* Emulation for that target is very fast. */
154 return 10000;
155 }
156
157 return 5000;
158 }
159
test_stress(uint32_t delay)160 static void test_stress(uint32_t delay)
161 {
162 uint32_t preempt = 2000;
163 uint32_t ctx_cnt = 3;
164
165 memset(&mock_backend, 0, sizeof(mock_backend));
166 log_process_delay = delay;
167 ztress_set_timeout(K_MSEC(get_test_timeout()));
168 ZTRESS_EXECUTE(
169 ZTRESS_TIMER(context_handler, NULL, 0, Z_TIMEOUT_TICKS(30)),
170 ZTRESS_THREAD(context_handler, NULL, 0, preempt, Z_TIMEOUT_TICKS(30)),
171 ZTRESS_THREAD(context_handler, NULL, 0, preempt, Z_TIMEOUT_TICKS(30)));
172
173 while (log_data_pending()) {
174 k_msleep(200);
175 }
176
177 /* Log last messages, they should not be dropped as all messages are processed earlier. */
178 for (int i = 0; i < ctx_cnt; i++) {
179 uint32_t data = (i << CNT_BITS) | ztress_exec_count(i);
180
181 LOG_INF("%u", data);
182 }
183
184 while (log_data_pending()) {
185 k_msleep(100);
186 }
187
188 k_msleep(10);
189
190 validate(ctx_cnt);
191 }
192
ZTEST(log_stress,test_stress_fast_processing)193 ZTEST(log_stress, test_stress_fast_processing)
194 {
195 test_stress(10);
196 }
197
ZTEST(log_stress,test_stress_slow_processing)198 ZTEST(log_stress, test_stress_slow_processing)
199 {
200 test_stress(100);
201 }
202
setup(void)203 static void *setup(void)
204 {
205 test_source_id = log_source_id_get(STRINGIFY(MODULE_NAME));
206
207 return NULL;
208 }
209
before(void * data)210 static void before(void *data)
211 {
212 ARG_UNUSED(data);
213
214 ztest_simple_1cpu_before(data);
215 }
216
after(void * data)217 static void after(void *data)
218 {
219 ARG_UNUSED(data);
220
221 ztest_simple_1cpu_after(data);
222 }
223
teardown(void * data)224 static void teardown(void *data)
225 {
226 ARG_UNUSED(data);
227
228 log_backend_disable(&test);
229 }
230
231 ZTEST_SUITE(log_stress, NULL, setup, before, after, teardown);
232