1 /*
2 * Copyright (c) 2021 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 * @brief Test log benchmark
10 *
11 */
12
13
14 #include <tc_util.h>
15 #include <stdbool.h>
16 #include <zephyr.h>
17 #include <ztest.h>
18 #include <logging/log_backend.h>
19 #include <logging/log_ctrl.h>
20 #include <logging/log.h>
21 #include "test_helpers.h"
22
23 #define LOG_MODULE_NAME test
24 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
25
26 #if LOG_BENCHMARK_DETAILED_PRINT
27 #define DBG_PRINT(...) PRINT(__VA_ARGS__)
28 #else
29 #define DBG_PRINT(...)
30 #endif
31
32 typedef void (*custom_put_callback_t)(struct log_backend const *const backend,
33 struct log_msg *msg, size_t counter);
34
35 struct backend_cb {
36 size_t counter;
37 bool panic;
38 bool keep_msgs;
39 bool check_id;
40 uint32_t exp_id[100];
41 bool check_timestamp;
42 uint32_t exp_timestamps[100];
43 bool check_args;
44 uint32_t exp_nargs[100];
45 bool check_strdup;
46 bool exp_strdup[100];
47 custom_put_callback_t callback;
48 uint32_t total_drops;
49 };
50
put(struct log_backend const * const backend,struct log_msg * msg)51 static void put(struct log_backend const *const backend,
52 struct log_msg *msg)
53 {
54 log_msg_get(msg);
55 log_msg_put(msg);
56 }
57
process(struct log_backend const * const backend,union log_msg2_generic * msg)58 static void process(struct log_backend const *const backend,
59 union log_msg2_generic *msg)
60 {
61 }
62
panic(struct log_backend const * const backend)63 static void panic(struct log_backend const *const backend)
64 {
65 struct backend_cb *cb = (struct backend_cb *)backend->cb->ctx;
66
67 cb->panic = true;
68 }
69
dropped(struct log_backend const * const backend,uint32_t cnt)70 static void dropped(struct log_backend const *const backend, uint32_t cnt)
71 {
72 struct backend_cb *cb = (struct backend_cb *)backend->cb->ctx;
73
74 cb->total_drops += cnt;
75 }
76
77 const struct log_backend_api log_backend_test_api = {
78 .put = IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) ? put : NULL,
79 .process = IS_ENABLED(CONFIG_LOG2) ? process : NULL,
80 .panic = panic,
81 .dropped = dropped,
82 };
83
84 LOG_BACKEND_DEFINE(backend, log_backend_test_api, false);
85 struct backend_cb backend_ctrl_blk;
86
87 #define TEST_FORMAT_SPEC(i, _) " %d"
88 #define TEST_VALUE(i, _), i
89
90 /** @brief Count log messages until first drop.
91 *
92 * Report number of messages that fit in the buffer.
93 *
94 * @param nargs Number of int arguments in the log message.
95 */
96 #define TEST_LOG_CAPACITY(nargs, inc_cnt, _print) do { \
97 int _cnt = 0; \
98 test_helpers_log_setup(); \
99 while (!test_helpers_log_dropped_pending()) { \
100 LOG_ERR("test" UTIL_LISTIFY(nargs, TEST_FORMAT_SPEC) \
101 UTIL_LISTIFY(nargs, TEST_VALUE)); \
102 _cnt++; \
103 } \
104 _cnt--; \
105 inc_cnt += _cnt; \
106 if (_print) { \
107 DBG_PRINT("%d log message with %d arguments fit in %d space.\n", \
108 _cnt, nargs, CONFIG_LOG_BUFFER_SIZE); \
109 } \
110 } while (0)
111
112 /** Test how many messages fits in the logging buffer in deferred mode. Test
113 * serves as the comparison between logging versions.
114 */
test_log_capacity(void)115 void test_log_capacity(void)
116 {
117 int total_cnt = 0;
118
119 TEST_LOG_CAPACITY(0, total_cnt, 1);
120 TEST_LOG_CAPACITY(1, total_cnt, 1);
121 TEST_LOG_CAPACITY(2, total_cnt, 1);
122 TEST_LOG_CAPACITY(3, total_cnt, 1);
123 TEST_LOG_CAPACITY(4, total_cnt, 1);
124 TEST_LOG_CAPACITY(5, total_cnt, 1);
125 TEST_LOG_CAPACITY(6, total_cnt, 1);
126 TEST_LOG_CAPACITY(7, total_cnt, 1);
127 TEST_LOG_CAPACITY(8, total_cnt, 1);
128
129 PRINT("In total %d message were stored.\n", total_cnt);
130 }
131
132 #define TEST_LOG_MESSAGE_STORE_NO_OVERFLOW(nargs, inc_time, inc_msg) do { \
133 int _msg_cnt = 0; \
134 TEST_LOG_CAPACITY(nargs, _msg_cnt, 0); \
135 test_helpers_log_setup(); \
136 uint32_t cyc = test_helpers_cycle_get(); \
137 for (int i = 0; i < _msg_cnt; i++) { \
138 LOG_ERR("test" UTIL_LISTIFY(nargs, TEST_FORMAT_SPEC) \
139 UTIL_LISTIFY(nargs, TEST_VALUE)); \
140 } \
141 cyc = test_helpers_cycle_get() - cyc; \
142 inc_time += cyc; \
143 inc_msg += _msg_cnt; \
144 DBG_PRINT("%d arguments message logged in %u cycles (%u us). " \
145 "%d message logged in %u cycles.\n", \
146 nargs, cyc / _msg_cnt, k_cyc_to_us_ceil32(cyc) / _msg_cnt, \
147 _msg_cnt, cyc); \
148 } while (0)
149
test_log_message_store_time_no_overwrite(void)150 void test_log_message_store_time_no_overwrite(void)
151 {
152 uint32_t total_cyc = 0;
153 uint32_t total_msg = 0;
154
155 TEST_LOG_MESSAGE_STORE_NO_OVERFLOW(0, total_cyc, total_msg);
156 TEST_LOG_MESSAGE_STORE_NO_OVERFLOW(1, total_cyc, total_msg);
157 TEST_LOG_MESSAGE_STORE_NO_OVERFLOW(2, total_cyc, total_msg);
158 TEST_LOG_MESSAGE_STORE_NO_OVERFLOW(3, total_cyc, total_msg);
159 TEST_LOG_MESSAGE_STORE_NO_OVERFLOW(4, total_cyc, total_msg);
160 TEST_LOG_MESSAGE_STORE_NO_OVERFLOW(5, total_cyc, total_msg);
161 TEST_LOG_MESSAGE_STORE_NO_OVERFLOW(6, total_cyc, total_msg);
162 TEST_LOG_MESSAGE_STORE_NO_OVERFLOW(7, total_cyc, total_msg);
163 TEST_LOG_MESSAGE_STORE_NO_OVERFLOW(8, total_cyc, total_msg);
164
165 uint32_t total_us = k_cyc_to_us_ceil32(total_cyc);
166
167 PRINT("%sAvarage logging a message: %u cycles (%u us)\n",
168 k_is_user_context() ? "USERSPACE: " : "",
169 total_cyc / total_msg, total_us / total_msg);
170 }
171
172 #define TEST_LOG_MESSAGE_STORE_OVERFLOW(nargs, _msg_cnt, inc_time, inc_msg) do { \
173 int _dummy = 0; \
174 /* Saturate buffer. */ \
175 TEST_LOG_CAPACITY(nargs, _dummy, 0); \
176 uint32_t cyc = test_helpers_cycle_get(); \
177 for (int i = 0; i < _msg_cnt; i++) { \
178 LOG_ERR("test" UTIL_LISTIFY(nargs, TEST_FORMAT_SPEC) \
179 UTIL_LISTIFY(nargs, TEST_VALUE)); \
180 } \
181 cyc = test_helpers_cycle_get() - cyc; \
182 inc_time += cyc; \
183 inc_msg += _msg_cnt; \
184 DBG_PRINT("%d arguments message logged in %u cycles (%u us). " \
185 "%d message logged in %u cycles.\n", \
186 nargs, cyc / _msg_cnt, k_cyc_to_us_ceil32(cyc) / _msg_cnt, \
187 _msg_cnt, cyc); \
188 } while (0)
189
test_log_message_store_time_overwrite(void)190 void test_log_message_store_time_overwrite(void)
191 {
192 uint32_t total_cyc = 0;
193 uint32_t total_msg = 0;
194
195 TEST_LOG_MESSAGE_STORE_OVERFLOW(0, 50, total_cyc, total_msg);
196 TEST_LOG_MESSAGE_STORE_OVERFLOW(1, 50, total_cyc, total_msg);
197 TEST_LOG_MESSAGE_STORE_OVERFLOW(2, 50, total_cyc, total_msg);
198 TEST_LOG_MESSAGE_STORE_OVERFLOW(3, 50, total_cyc, total_msg);
199 TEST_LOG_MESSAGE_STORE_OVERFLOW(4, 50, total_cyc, total_msg);
200 TEST_LOG_MESSAGE_STORE_OVERFLOW(5, 50, total_cyc, total_msg);
201 TEST_LOG_MESSAGE_STORE_OVERFLOW(6, 50, total_cyc, total_msg);
202 TEST_LOG_MESSAGE_STORE_OVERFLOW(7, 50, total_cyc, total_msg);
203 TEST_LOG_MESSAGE_STORE_OVERFLOW(8, 50, total_cyc, total_msg);
204
205 uint32_t total_us = k_cyc_to_us_ceil32(total_cyc);
206
207 PRINT("Avarage overwrite logging a message: %u cycles (%u us)\n",
208 total_cyc / total_msg, total_us / total_msg);
209 }
210
test_log_message_store_time_no_overwrite_from_user(void)211 void test_log_message_store_time_no_overwrite_from_user(void)
212 {
213 if (!IS_ENABLED(CONFIG_USERSPACE)) {
214 printk("no userspace\n");
215 return;
216 }
217
218 test_log_message_store_time_no_overwrite();
219 }
220
test_log_message_with_string(void)221 void test_log_message_with_string(void)
222 {
223 test_helpers_log_setup();
224 char strbuf[] = "test string";
225 uint32_t cyc = test_helpers_cycle_get();
226 int repeat = 8;
227
228 for (int i = 0; i < repeat; i++) {
229 LOG_ERR("test with string to duplicate: %s", log_strdup(strbuf));
230 }
231
232 cyc = test_helpers_cycle_get() - cyc;
233 uint32_t us = k_cyc_to_us_ceil32(cyc);
234
235 PRINT("%slogging with transient string %u cycles (%u us).",
236 k_is_user_context() ? "USERSPACE: " : "",
237 cyc / repeat, us / repeat);
238 }
239
240 /*test case main entry*/
test_main(void)241 void test_main(void)
242 {
243 PRINT("LOGGING MODE:%s\n", IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) ? "DEFERREDv1" :
244 (IS_ENABLED(CONFIG_LOG2_MODE_DEFERRED) ? "DEFERREDv2" :
245 (IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? "IMMEDIATEv1" :
246 "IMMEDIATEv2")));
247 PRINT("\tOVERWRITE: %d\n", IS_ENABLED(CONFIG_LOG_MODE_OVERFLOW));
248 PRINT("\tBUFFER_SIZE: %d\n", CONFIG_LOG_BUFFER_SIZE);
249 if (IS_ENABLED(CONFIG_LOG2_MODE_DEFERRED)) {
250 PRINT("\tSPEED: %d", IS_ENABLED(CONFIG_LOG_SPEED));
251 }
252 ztest_test_suite(test_log_benchmark,
253 ztest_unit_test(test_log_capacity),
254 ztest_unit_test(test_log_message_store_time_no_overwrite),
255 ztest_unit_test(test_log_message_store_time_overwrite),
256 ztest_user_unit_test(test_log_message_store_time_no_overwrite_from_user),
257 ztest_user_unit_test(test_log_message_with_string)
258 );
259 ztest_run_test_suite(test_log_benchmark);
260 }
261