/* * Copyright (c) 2022 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #define MODULE_NAME test LOG_MODULE_REGISTER(MODULE_NAME); static uint32_t test_source_id; #define CNT_BITS 28 struct mock_log_backend { uint32_t last_id[16]; uint32_t cnt[16]; uint32_t dropped; uint32_t missing; }; static struct mock_log_backend mock_backend; static uint32_t log_process_delay = 10; static void handle_msg(uint32_t arg0) { uint32_t ctx_id = arg0 >> CNT_BITS; uint32_t id = arg0 & BIT_MASK(CNT_BITS); int off = id - mock_backend.last_id[ctx_id] - 1; mock_backend.cnt[ctx_id]++; k_busy_wait(log_process_delay); if (off > 0) { mock_backend.missing += off; } mock_backend.last_id[ctx_id] = id; } static void process(const struct log_backend *const backend, union log_msg_generic *msg) { size_t len; uint8_t *package = log_msg_get_package(&msg->log, &len); package += 2 * sizeof(void *); handle_msg(*(uint32_t *)package); } static void mock_init(struct log_backend const *const backend) { } static void panic(struct log_backend const *const backend) { zassert_true(false); } static void dropped(const struct log_backend *const backend, uint32_t cnt) { mock_backend.dropped += cnt; } static const struct log_backend_api log_backend_api = { .process = process, .panic = panic, .init = mock_init, .dropped = IS_ENABLED(CONFIG_LOG_MODE_DEFERRED) ? dropped : NULL, }; LOG_BACKEND_DEFINE(test, log_backend_api, true, NULL); static void validate(int ctx_cnt) { uint64_t in_cnt = 0; uint64_t out_cnt = 0; for (int i = 0; i < ctx_cnt; i++) { /* First exectution skips (-1) but there is one final round of logging * when ztress execution is completed (+1). */ in_cnt += ztress_exec_count(i) - 1 + 1; out_cnt += mock_backend.cnt[i]; } out_cnt += mock_backend.dropped; zassert_equal(mock_backend.dropped, mock_backend.missing, "dropped:%u missing:%u", mock_backend.dropped, mock_backend.missing); zassert_equal(in_cnt, out_cnt); } static bool context_handler(void *user_data, uint32_t cnt, bool last, int prio) { /* Skip first execution to start from id = 1. That simplifies handling in * the backend and validation of message. */ if (cnt == 0) { return true; } uint32_t i = cnt | (prio << CNT_BITS); switch (sys_rand8_get() % 4) { case 0: LOG_INF("%u", i); break; case 1: LOG_INF("%u %u %u %u", i, 1, 2, 3); break; case 2: { char test_str[] = "test string"; LOG_INF("%u %s %s", i, "test", test_str); break; } default: LOG_INF("%u %d", i, 100); break; } return true; } static int get_test_timeout(void) { if (IS_ENABLED(CONFIG_BOARD_QEMU_CORTEX_A9)) { /* Emulation for that target is extremely slow. */ return 500; } if (IS_ENABLED(CONFIG_BOARD_QEMU_X86)) { /* Emulation for that target is very fast. */ return 10000; } if (IS_ENABLED(CONFIG_BOARD_QEMU_X86_64)) { /* Emulation for that target is very fast. */ return 10000; } return 5000; } static void test_stress(uint32_t delay) { uint32_t preempt = 2000; uint32_t ctx_cnt = 3; memset(&mock_backend, 0, sizeof(mock_backend)); log_process_delay = delay; ztress_set_timeout(K_MSEC(get_test_timeout())); ZTRESS_EXECUTE( ZTRESS_TIMER(context_handler, NULL, 0, Z_TIMEOUT_TICKS(30)), ZTRESS_THREAD(context_handler, NULL, 0, preempt, Z_TIMEOUT_TICKS(30)), ZTRESS_THREAD(context_handler, NULL, 0, preempt, Z_TIMEOUT_TICKS(30))); while (log_data_pending()) { k_msleep(200); } /* Log last messages, they should not be dropped as all messages are processed earlier. */ for (int i = 0; i < ctx_cnt; i++) { uint32_t data = (i << CNT_BITS) | ztress_exec_count(i); LOG_INF("%u", data); } while (log_data_pending()) { k_msleep(100); } k_msleep(10); validate(ctx_cnt); } ZTEST(log_stress, test_stress_fast_processing) { test_stress(10); } ZTEST(log_stress, test_stress_slow_processing) { test_stress(100); } static void *setup(void) { test_source_id = log_source_id_get(STRINGIFY(MODULE_NAME)); return NULL; } static void before(void *data) { ARG_UNUSED(data); ztest_simple_1cpu_before(data); } static void after(void *data) { ARG_UNUSED(data); ztest_simple_1cpu_after(data); } static void teardown(void *data) { ARG_UNUSED(data); log_backend_disable(&test); } ZTEST_SUITE(log_stress, NULL, setup, before, after, teardown);