diff --git a/tests/kernel/obj_core/obj_core_stats/CMakeLists.txt b/tests/kernel/obj_core/obj_core_stats/CMakeLists.txt new file mode 100644 index 00000000000..a9c26c7493c --- /dev/null +++ b/tests/kernel/obj_core/obj_core_stats/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(obj_core) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/kernel/obj_core/obj_core_stats/prj.conf b/tests/kernel/obj_core/obj_core_stats/prj.conf new file mode 100644 index 00000000000..47b02f61737 --- /dev/null +++ b/tests/kernel/obj_core/obj_core_stats/prj.conf @@ -0,0 +1,8 @@ +CONFIG_ZTEST=y +CONFIG_ZTEST_NEW_API=y +CONFIG_OBJ_CORE=y +CONFIG_OBJ_CORE_STATS=y +CONFIG_SCHED_THREAD_USAGE=y +CONFIG_SCHED_THREAD_USAGE_ANALYSIS=y +CONFIG_MEM_SLAB_TRACE_MAX_UTILIZATION=y +CONFIG_SYS_MEM_BLOCKS=y diff --git a/tests/kernel/obj_core/obj_core_stats/src/main.c b/tests/kernel/obj_core/obj_core_stats/src/main.c new file mode 100644 index 00000000000..24d649e363a --- /dev/null +++ b/tests/kernel/obj_core/obj_core_stats/src/main.c @@ -0,0 +1,704 @@ +/* + * Copyright (c) 2023, Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +SYS_MEM_BLOCKS_DEFINE(mem_block, 32, 4, 16); /* Four 32 byte blocks */ + +K_MEM_SLAB_DEFINE(mem_slab, 32, 4, 16); /* Four 32 byte blocks */ + +#if !defined(CONFIG_ARCH_POSIX) && !defined(CONFIG_SPARC) && !defined(CONFIG_MIPS) +static void test_thread_entry(void *, void *, void *); +K_THREAD_DEFINE(test_thread, 1024, test_thread_entry, NULL, NULL, NULL, + K_HIGHEST_THREAD_PRIO, 0, 0); + +K_SEM_DEFINE(wake_main_thread, 0, 1); +K_SEM_DEFINE(wake_test_thread, 0, 1); +#endif /* !CONFIG_ARCH_POSIX && !CONFIG_SPARC && !CONFIG_MIPS */ + +#if CONFIG_MP_MAX_NUM_CPUS > 1 +K_THREAD_STACK_ARRAY_DEFINE(busy_thread_stack, CONFIG_MP_MAX_NUM_CPUS - 1, 512); + +struct k_thread busy_thread[CONFIG_MP_MAX_NUM_CPUS - 1]; + +void busy_thread_entry(void *p1, void *p2, void *p3) +{ + while (1) { + /* Busy loop to prevent CPU from entering idle */ + } +} + +#endif + +/***************** SYSTEM (CPUs and KERNEL) ******************/ + +/* + * As the k_obj_core_stats_xxx() APIs are essentially wrappers to the + * thread runtime stats APIs, limit this test to the same architectures as + * that thread runtime stats test. + */ + +#if !defined(CONFIG_ARCH_POSIX) && !defined(CONFIG_SPARC) && !defined(CONFIG_MIPS) +ZTEST(obj_core_stats_system, test_obj_core_stats_system) +{ + int status; + struct k_cycle_stats kernel_raw[CONFIG_MP_MAX_NUM_CPUS]; + struct k_cycle_stats cpu_raw; + struct k_thread_runtime_stats kernel_query; + struct k_thread_runtime_stats cpu_query; + struct k_thread_runtime_stats sum_query; + unsigned int i; + +#if CONFIG_MP_MAX_NUM_CPUS > 1 + + /* Create 1 busy thread for each core except the current */ + + int prio; + + prio = k_thread_priority_get(k_current_get()); + + for (i = 0; i < CONFIG_MP_MAX_NUM_CPUS - 1; i++) { + k_thread_create(&busy_thread[i], busy_thread_stack[i], + K_THREAD_STACK_SIZEOF(busy_thread_stack[i]), + busy_thread_entry, NULL, NULL, NULL, + prio + 10, 0, K_NO_WAIT); + } +#endif + + status = k_obj_core_stats_raw(K_OBJ_CORE(&_kernel), kernel_raw, + sizeof(kernel_raw)); + zassert_equal(status, 0, "Expected 0, got %d\n", status); + + /* + * Not much can be predicted for the raw stats aside from the + * the contents of the CPU sampling to be at least as large as + * kernel sampling. The same goes for the query stats. + */ + + for (i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { + status = k_obj_core_stats_raw(K_OBJ_CORE(&_kernel.cpus[i]), + &cpu_raw, sizeof(cpu_raw)); + zassert_equal(status, 0, "Expected 0, got %d on CPU %u\n", + status, i); + + zassert_true(cpu_raw.total >= kernel_raw[i].total); +#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS + zassert_true(cpu_raw.current >= kernel_raw[i].current); + zassert_true(cpu_raw.longest >= kernel_raw[i].longest); + zassert_true(cpu_raw.num_windows >= kernel_raw[i].num_windows); +#endif + zassert_true(cpu_raw.track_usage == kernel_raw[i].track_usage); + } + + status = k_obj_core_stats_query(K_OBJ_CORE(&_kernel), &kernel_query, + sizeof(kernel_query)); + zassert_equal(status, 0, "Expected 0, got %d\n", status); + + sum_query = (struct k_thread_runtime_stats){}; + + for (i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { + status = k_obj_core_stats_query(K_OBJ_CORE(&_kernel.cpus[i]), + &cpu_query, sizeof(cpu_query)); + zassert_equal(status, 0, "Expected 0, got %d on CPU %u\n", + status, i); + +#ifdef CONFIG_SCHED_THREAD_USAGE + sum_query.execution_cycles += cpu_query.execution_cycles; + sum_query.total_cycles += cpu_query.total_cycles; +#endif +#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS + sum_query.current_cycles += cpu_query.current_cycles; + sum_query.peak_cycles += cpu_query.peak_cycles; + sum_query.average_cycles += cpu_query.average_cycles; +#endif +#ifdef CONFIG_SCHED_THREAD_USAGE_ALL + sum_query.idle_cycles += cpu_query.idle_cycles; +#endif + } + +#ifdef CONFIG_SCHED_THREAD_USAGE + zassert_true(sum_query.execution_cycles >= kernel_query.execution_cycles); + zassert_true(sum_query.total_cycles >= kernel_query.total_cycles); +#endif +#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS + zassert_true(sum_query.current_cycles >= kernel_query.current_cycles); + zassert_true(sum_query.peak_cycles >= kernel_query.peak_cycles); + zassert_true(sum_query.average_cycles >= kernel_query.average_cycles); +#endif +#ifdef CONFIG_SCHED_THREAD_USAGE_ALL + zassert_true(sum_query.idle_cycles >= kernel_query.idle_cycles); +#endif +} +#endif /* !CONFIG_ARCH_POSIX && !CONFIG_SPARC && !CONFIG_MIPS */ + +ZTEST(obj_core_stats_system, test_obj_core_stats_cpu_reset) +{ + int status; + + for (unsigned int i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { + status = k_obj_core_stats_reset(K_OBJ_CORE(&_kernel.cpus[i])); + zassert_equal(status, -ENOTSUP, + "Expected %d, got %d on CPU%d\n", + -ENOTSUP, status, i); + } +} + +ZTEST(obj_core_stats_system, test_obj_core_stats_cpu_disable) +{ + int status; + + for (unsigned int i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { + status = k_obj_core_stats_disable(K_OBJ_CORE(&_kernel.cpus[i])); + zassert_equal(status, -ENOTSUP, + "Expected %d, got %d on CPU%d\n", + -ENOTSUP, status, i); + } +} + +ZTEST(obj_core_stats_system, test_obj_core_stats_cpu_enable) +{ + int status; + + for (unsigned int i = 0; i < CONFIG_MP_MAX_NUM_CPUS; i++) { + status = k_obj_core_stats_enable(K_OBJ_CORE(&_kernel.cpus[i])); + zassert_equal(status, -ENOTSUP, + "Expected %d, got %d on CPU%d\n", + -ENOTSUP, status, i); + } +} + +ZTEST(obj_core_stats_system, test_obj_core_stats_kernel_reset) +{ + int status; + + status = k_obj_core_stats_reset(K_OBJ_CORE(&_kernel)); + zassert_equal(status, -ENOTSUP, "Expected %d, got %d\n", + -ENOTSUP, status); +} + +ZTEST(obj_core_stats_system, test_obj_core_stats_kernel_disable) +{ + int status; + + status = k_obj_core_stats_disable(K_OBJ_CORE(&_kernel)); + zassert_equal(status, -ENOTSUP, "Expected %d, got %d\n", + -ENOTSUP, status); +} + +ZTEST(obj_core_stats_system, test_obj_core_stats_kernel_enable) +{ + int status; + + status = k_obj_core_stats_enable(K_OBJ_CORE(&_kernel)); + zassert_equal(status, -ENOTSUP, "Expected %d, got %d\n", + -ENOTSUP, status); +} + +/***************** THREADS ******************/ + +#if !defined(CONFIG_ARCH_POSIX) && !defined(CONFIG_SPARC) && !defined(CONFIG_MIPS) +/* + * As the k_obj_core_stats_xxx() APIs are essentially wrappers to the + * thread runtime stats APIs, limit this test to the same architectures as + * that thread runtime stats test. + */ +void test_thread_entry(void *p1, void *p2, void *p3) +{ + while (1) { + k_busy_wait(10000); + + k_sem_give(&wake_main_thread); + k_sem_take(&wake_test_thread, K_FOREVER); + } +} + +ZTEST(obj_core_stats_thread, test_obj_core_stats_thread_test) +{ + struct k_cycle_stats raw1; + struct k_cycle_stats raw2; + struct k_thread_runtime_stats query1; + struct k_thread_runtime_stats query2; + struct k_thread_runtime_stats query3; + int status; + + k_sem_take(&wake_main_thread, K_FOREVER); + k_busy_wait(10000); + + /* test_thread should now be blocked on wake_test_thread */ + + status = k_obj_core_stats_raw(K_OBJ_CORE(test_thread), &raw1, + sizeof(raw1)); + zassert_equal(status, 0, "Expected 0, got %d", status); + + status = k_obj_core_stats_query(K_OBJ_CORE(test_thread), &query1, + sizeof(query1)); + zassert_equal(status, 0, "Expected 0, got %d", status); + + /* + * Busy wait for 10 msec. As test_thread should still be blocked, + * its stats data should not change. + */ + + k_busy_wait(10000); + + status = k_obj_core_stats_raw(K_OBJ_CORE(test_thread), &raw2, + sizeof(raw2)); + zassert_equal(status, 0, "Expected 0, got %d", status); + + status = k_obj_core_stats_query(K_OBJ_CORE(test_thread), &query2, + sizeof(query2)); + zassert_equal(status, 0, "Expected 0, got %d", status); + + zassert_mem_equal(&raw1, &raw2, sizeof(raw1), + "Thread raw stats changed while blocked\n"); + zassert_mem_equal(&query1, &query2, sizeof(query1), + "Thread query stats changed while blocked\n"); + + /* + * Let test_thread execute for a short bit and then re-sample the + * stats. As the k_obj_core_stats_query() backend is identical to + * that of k_thread_runtime_stats_get(), their queries should be + * identical (and different from the previous sample). + */ + + k_sem_give(&wake_test_thread); + k_sem_take(&wake_main_thread, K_FOREVER); + k_busy_wait(10000); + + /* test_thread should now be blocked. */ + + status = k_obj_core_stats_query(K_OBJ_CORE(test_thread), &query2, + sizeof(query3)); + zassert_equal(status, 0, "Expected 0, got %d\n", status); + + status = k_thread_runtime_stats_get(test_thread, &query3); + zassert_equal(status, 0, "Expected 0, got %d\n", status); + zassert_mem_equal(&query2, &query3, sizeof(query2), + "Queries not equal!\n"); + +#ifdef CONFIG_SCHED_THREAD_USAGE + zassert_true(query2.execution_cycles > query1.execution_cycles, + "Execution cycles did not increase\n"); + zassert_true(query2.total_cycles > query1.total_cycles, + "Total cycles did not increase\n"); +#endif + +#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS + + /* + * [current_cycles], [peak_cycles] and [average_cycles] can not be + * predicted by this test. + */ + +#endif + +#ifdef CONFIG_SCHED_THREAD_USAGE_ALL + zassert_equal(query2.idle_cycles, 0, + "Expected 0, got %llu\n", query2.idle_cycles); +#endif + + /* Reset the stats */ + + status = k_obj_core_stats_reset(K_OBJ_CORE(test_thread)); + zassert_equal(status, 0, "Expected 0, got %d\n", status); + + status = k_obj_core_stats_query(K_OBJ_CORE(test_thread), + &query3, sizeof(query3)); + zassert_equal(status, 0, "Expected 0, got %d\n", status); + +#ifdef CONFIG_SCHED_THREAD_USAGE + zassert_equal(query3.execution_cycles, 0, + "Expected 0, got %llu\n", query3.execution_cycles); + zassert_equal(query3.total_cycles, 0, + "Expected 0, got %llu\n", query3.total_cycles); +#endif + +#ifdef CONFIG_SCHED_THREAD_USAGE_ANALYSIS + zassert_equal(query3.current_cycles, 0, + "Expected 0, got %llu\n", query3.current_cycles); + zassert_equal(query3.peak_cycles, 0, + "Expected 0, got %llu\n", query3.peak_cycles); + zassert_equal(query3.average_cycles, 0, + "Expected 0, got %llu\n", query3.average_cycles); +#endif + +#ifdef CONFIG_SCHED_THREAD_USAGE_ALL + zassert_equal(query3.idle_cycles, 0, + "Expected 0, got %llu\n", query3.idle_cycles); +#endif + + /* Disable the stats (re-using query2 and query3) */ + + status = k_obj_core_stats_disable(K_OBJ_CORE(test_thread)); + zassert_equal(status, 0, "Expected 0, got %llu\n", status); + + k_sem_give(&wake_test_thread); + k_sem_take(&wake_main_thread, K_FOREVER); + k_busy_wait(10000); + + k_obj_core_stats_query(K_OBJ_CORE(test_thread), + &query2, sizeof(query2)); + + zassert_mem_equal(&query2, &query3, sizeof(query2), + "Stats changed while disabled!\n"); + + /* Enable the stats */ + + status = k_obj_core_stats_enable(K_OBJ_CORE(test_thread)); + zassert_equal(status, 0, "Expected 0, got %llu\n", status); + + k_sem_give(&wake_test_thread); + k_sem_take(&wake_main_thread, K_FOREVER); + k_busy_wait(10000); + + k_obj_core_stats_query(K_OBJ_CORE(test_thread), + &query3, sizeof(query3)); + + /* We can not predict the stats, but they should be non-zero. */ + +#ifdef CONFIG_SCHED_THREAD_USAGE + zassert_true(query3.execution_cycles > 0); + zassert_true(query3.total_cycles > 0); +#endif +#ifdef CONFIG_SCHED_THREAD_USAGE + zassert_true(query3.current_cycles > 0); + zassert_true(query3.peak_cycles > 0); + zassert_true(query3.average_cycles > 0); +#endif +#ifdef CONFIG_SCHED_THREAD_USAGE_ALL + zassert_true(query3.idle_cycles == 0); +#endif + + k_thread_abort(test_thread); +} +#endif /* !CONFIG_ARCH_POSIX && !CONFIG_SPARC && !CONFIG_MIPS */ + +/***************** SYSTEM MEMORY BLOCKS *********************/ + +ZTEST(obj_core_stats_mem_block, test_sys_mem_block_enable) +{ + int status; + + status = k_obj_core_stats_enable(K_OBJ_CORE(&mem_block)); + zassert_equal(status, -ENOTSUP, + "Not supposed to be supported. Got %d, not %d\n", + status, -ENOTSUP); +} + +ZTEST(obj_core_stats_mem_block, test_sys_mem_block_disable) +{ + int status; + + status = k_obj_core_stats_disable(K_OBJ_CORE(&mem_block)); + zassert_equal(status, -ENOTSUP, + "Not supposed to be supported. Got %d, not %d\n", + status, -ENOTSUP); +} + +static void test_mem_block_raw(const char *str, + struct sys_mem_blocks_info *expected) +{ + int status; + struct sys_mem_blocks_info raw; + + status = k_obj_core_stats_raw(K_OBJ_CORE(&mem_block), &raw, + sizeof(raw)); + zassert_equal(status, 0, + "%s: Failed to get raw stats (%d)\n", str, status); + + zassert_equal(raw.num_blocks, expected->num_blocks, + "%s: Expected %u blocks, got %u\n", + str, expected->num_blocks, raw.num_blocks); + zassert_equal(raw.blk_sz_shift, expected->blk_sz_shift, + "%s: Expected blk_sz_shift=%u, got %u\n", + str, expected->blk_sz_shift, raw.blk_sz_shift); +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + zassert_equal(raw.used_blocks, expected->used_blocks, + "%s: Expected %u used, got %d\n", + str, expected->used_blocks, raw.used_blocks); + zassert_equal(raw.max_used_blocks, expected->max_used_blocks, + "%s: Expected max %u used, got %d\n", + str, expected->max_used_blocks, raw.max_used_blocks); +#endif +} + +static void test_mem_block_query(const char *str, + struct sys_memory_stats *expected) +{ + struct sys_memory_stats query; + int status; + + status = k_obj_core_stats_query(K_OBJ_CORE(&mem_block), &query, + sizeof(query)); + zassert_equal(status, 0, + "%s: Failed to get query stats (%d)\n", str, status); + + zassert_equal(query.free_bytes, expected->free_bytes, + "%s: Expected %u free bytes, got %u\n", + str, expected->free_bytes, query.free_bytes); +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + zassert_equal(query.allocated_bytes, expected->allocated_bytes, + "%s: Expected %u allocated bytes, got %u\n", + str, expected->allocated_bytes, query.allocated_bytes); + zassert_equal(query.max_allocated_bytes, expected->max_allocated_bytes, + "%s: Expected %u max_allocated bytes, got %d\n", + str, expected->max_allocated_bytes, + query.max_allocated_bytes); +#endif +} + +ZTEST(obj_core_stats_mem_block, test_obj_core_stats_mem_block) +{ + struct sys_mem_blocks_info raw = { + .num_blocks = 4, .blk_sz_shift = 5, +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + .used_blocks = 0, .max_used_blocks = 0 +#endif + }; + struct sys_memory_stats query = { + .free_bytes = 128, + .allocated_bytes = 0, + .max_allocated_bytes = 0 + }; + void *mem1; + void *mem2; + int status; + + /* + * As the ordering of the "raw", "query" and "reset" tests matter, + * they have been grouped together here. As they are for the most + * wrappers for the runtime stats routines, minimal testing is + * being done. + */ + + /* Initial checks */ + + test_mem_block_raw("Initial", &raw); + test_mem_block_query("Initial", &query); + + /* Allocate 1st block */ + + status = sys_mem_blocks_alloc(&mem_block, 1, &mem1); + zassert_equal(status, 0, "Expected 0, got %d\n", status); + + query.free_bytes -= 32; + query.allocated_bytes += 32; +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + raw.used_blocks++; + raw.max_used_blocks++; + query.max_allocated_bytes += 32; +#endif + test_mem_block_raw("1st Alloc", &raw); + test_mem_block_query("1st Alloc", &query); + + /* Allocate 2nd block */ + + status = sys_mem_blocks_alloc(&mem_block, 1, &mem2); + zassert_equal(status, 0, "Expected 0, got %d\n", status); + + query.free_bytes -= 32; + query.allocated_bytes += 32; +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + raw.used_blocks++; + raw.max_used_blocks++; + query.max_allocated_bytes += 32; +#endif + test_mem_block_raw("2nd Alloc", &raw); + test_mem_block_query("2nd Alloc", &query); + + /* Free 1st block */ + + sys_mem_blocks_free(&mem_block, 1, &mem1); + + query.free_bytes += 32; + query.allocated_bytes -= 32; +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + raw.used_blocks--; +#endif + test_mem_block_raw("Free 1st", &raw); + test_mem_block_query("Free 1st", &query); + + /* Reset the mem block stats */ + + status = k_obj_core_stats_reset(K_OBJ_CORE(&mem_block)); + zassert_equal(status, 0, "Expected 0, got %d\n", status); +#ifdef CONFIG_SYS_MEM_BLOCKS_RUNTIME_STATS + raw.max_used_blocks = raw.used_blocks; + query.max_allocated_bytes = query.allocated_bytes; +#endif + test_mem_block_raw("Reset", &raw); + test_mem_block_query("Reset", &query); + + /* Cleanup - Free 2nd block */ + sys_mem_blocks_free(&mem_block, 1, &mem2); +} + +/***************** MEMORY SLABS *********************/ + +ZTEST(obj_core_stats_mem_slab, test_mem_slab_enable) +{ + int status; + + status = k_obj_core_stats_disable(K_OBJ_CORE(&mem_slab)); + zassert_equal(status, -ENOTSUP, + "Not supposed to be supported. Got %d, not %d\n", + status, -ENOTSUP); +} + +ZTEST(obj_core_stats_mem_slab, test_mem_slab_disable) +{ + int status; + + status = k_obj_core_stats_disable(K_OBJ_CORE(&mem_slab)); + zassert_equal(status, -ENOTSUP, + "Not supposed to be supported. Got %d, not %d\n", + status, -ENOTSUP); +} + +static void test_mem_slab_raw(const char *str, struct k_mem_slab_info *expected) +{ + int status; + struct k_mem_slab_info raw; + + status = k_obj_core_stats_raw(K_OBJ_CORE(&mem_slab), &raw, + sizeof(raw)); + zassert_equal(status, 0, + "%s: Failed to get raw stats (%d)\n", str, status); + + zassert_equal(raw.num_blocks, expected->num_blocks, + "%s: Expected %u blocks, got %u\n", + str, expected->num_blocks, raw.num_blocks); + zassert_equal(raw.block_size, expected->block_size, + "%s: Expected block size=%u blocks, got %u\n", + str, expected->block_size, raw.block_size); + zassert_equal(raw.num_used, expected->num_used, + "%s: Expected %u used, got %d\n", + str, expected->num_used, raw.num_used); +#ifdef CONFIG_MEM_SLAB_TRACE_MAX_UTILIZATION + zassert_equal(raw.max_used, expected->max_used, + "%s: Expected max %u used, got %d\n", + str, expected->max_used, raw.max_used); +#endif +} + +static void test_mem_slab_query(const char *str, + struct sys_memory_stats *expected) +{ + struct sys_memory_stats query; + int status; + + status = k_obj_core_stats_query(K_OBJ_CORE(&mem_slab), &query, + sizeof(query)); + zassert_equal(status, 0, + "%s: Failed to get query stats (%d)\n", str, status); + + zassert_equal(query.free_bytes, expected->free_bytes, + "%s: Expected %u free bytes, got %u\n", + str, expected->free_bytes, query.free_bytes); + zassert_equal(query.allocated_bytes, expected->allocated_bytes, + "%s: Expected %u allocated bytes, got %u\n", + str, expected->allocated_bytes, query.allocated_bytes); + zassert_equal(query.max_allocated_bytes, expected->max_allocated_bytes, + "%s: Expected %u max_allocated bytes, got %d\n", + str, expected->max_allocated_bytes, + query.max_allocated_bytes); +} + +ZTEST(obj_core_stats_mem_slab, test_obj_core_stats_mem_slab) +{ + struct k_mem_slab_info raw = { + .num_blocks = 4, .block_size = 32, .num_used = 0, +#ifdef CONFIG_MEM_SLAB_TRACE_MAX_UTILIZATION + .max_used = 0 +#endif + }; + struct sys_memory_stats query = { + .free_bytes = 128, + .allocated_bytes = 0, + .max_allocated_bytes = 0 + }; + void *mem1; + void *mem2; + int status; + + /* + * As the ordering of the "raw", "query" and "reset" tests matter, + * they have been grouped together here. As they are for the most + * wrappers for the runtime stats routines, minimal testing is + * being done. + */ + + + /* Initial checks */ + + test_mem_slab_raw("Initial", &raw); + test_mem_slab_query("Initial", &query); + + /* Allocate 1st block */ + + status = k_mem_slab_alloc(&mem_slab, &mem1, K_FOREVER); + zassert_equal(status, 0, "Expected 0, got %d\n", status); + + raw.num_used++; + query.free_bytes -= 32; + query.allocated_bytes += 32; +#ifdef CONFIG_MEM_SLAB_TRACE_MAX_UTILIZATION + raw.max_used++; + query.max_allocated_bytes += 32; +#endif + test_mem_slab_raw("1st Alloc", &raw); + test_mem_slab_query("1st Alloc", &query); + + /* Allocate 2nd block */ + + status = k_mem_slab_alloc(&mem_slab, &mem2, K_FOREVER); + zassert_equal(status, 0, "Expected 0, got %d\n", status); + + raw.num_used++; + query.free_bytes -= 32; + query.allocated_bytes += 32; +#ifdef CONFIG_MEM_SLAB_TRACE_MAX_UTILIZATION + raw.max_used++; + query.max_allocated_bytes += 32; +#endif + test_mem_slab_raw("2nd Alloc", &raw); + test_mem_slab_query("2nd Alloc", &query); + + /* Free 1st block */ + k_mem_slab_free(&mem_slab, mem1); + + raw.num_used--; + query.free_bytes += 32; + query.allocated_bytes -= 32; + test_mem_slab_raw("Free 1st", &raw); + test_mem_slab_query("Free 1st", &query); + + /* Reset the mem slab stats */ + status = k_obj_core_stats_reset(K_OBJ_CORE(&mem_slab)); + zassert_equal(status, 0, "Expected 0, got %d\n", status); +#ifdef CONFIG_MEM_SLAB_TRACE_MAX_UTILIZATION + raw.max_used = raw.num_used; + query.max_allocated_bytes = query.allocated_bytes; +#endif + test_mem_slab_raw("Reset", &raw); + test_mem_slab_query("Reset", &query); + + /* Cleanup - Free 2nd block */ + k_mem_slab_free(&mem_slab, mem2); +} + +ZTEST_SUITE(obj_core_stats_system, NULL, NULL, + ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL); + +ZTEST_SUITE(obj_core_stats_thread, NULL, NULL, + ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL); + +ZTEST_SUITE(obj_core_stats_mem_block, NULL, NULL, + ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL); + +ZTEST_SUITE(obj_core_stats_mem_slab, NULL, NULL, + ztest_simple_1cpu_before, ztest_simple_1cpu_after, NULL); diff --git a/tests/kernel/obj_core/obj_core_stats/testcase.yaml b/tests/kernel/obj_core/obj_core_stats/testcase.yaml new file mode 100644 index 00000000000..e2276a001b8 --- /dev/null +++ b/tests/kernel/obj_core/obj_core_stats/testcase.yaml @@ -0,0 +1,9 @@ +tests: + kernel.obj_core_stats: + tags: kernel + ignore_faults: true + integration_platforms: + - qemu_x86 + platform_exclude: + - qemu_x86_tiny + - qemu_x86_tiny@768