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