blob: 1e0e1e6f7d8c7e6ae314c418a4b9e7361daa1e82 [file] [log] [blame]
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <sys/util.h>
#include <string.h>
#include <ztress.h>
#include <random/rand32.h>
#include <logging/log.h>
#include <logging/log_ctrl.h>
#include <logging/log_backend.h>
#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, NULL);
}
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, NULL);
}
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_rand32_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);