diff --git a/tests/kernel/mem_protect/demand_paging/prj.conf b/tests/kernel/mem_protect/demand_paging/prj.conf index 06c2fde9b1f..bbdf6393496 100644 --- a/tests/kernel/mem_protect/demand_paging/prj.conf +++ b/tests/kernel/mem_protect/demand_paging/prj.conf @@ -1,2 +1,5 @@ CONFIG_ZTEST=y CONFIG_DEMAND_PAGING_STATS=y +CONFIG_DEMAND_PAGING_THREAD_STATS=y +CONFIG_DEMAND_PAGING_TIMING_HISTOGRAM=y +CONFIG_TEST_USERSPACE=y diff --git a/tests/kernel/mem_protect/demand_paging/src/main.c b/tests/kernel/mem_protect/demand_paging/src/main.c index 432b781ca92..818caaf2879 100644 --- a/tests/kernel/mem_protect/demand_paging/src/main.c +++ b/tests/kernel/mem_protect/demand_paging/src/main.c @@ -50,9 +50,30 @@ void test_map_anon_pages(void) z_page_frames_dump(); } +void print_paging_stats(struct k_mem_paging_stats_t *stats, const char *scope) +{ + printk("* Page Faults (%s):\n", scope); + printk(" - Total: %lu\n", stats->pagefaults.cnt); + printk(" - IRQ locked: %lu\n", stats->pagefaults.irq_locked); + printk(" - IRQ unlocked: %lu\n", stats->pagefaults.irq_unlocked); +#ifndef CONFIG_DEMAND_PAGING_ALLOW_IRQ + printk(" - in ISR: %lu\n", stats->pagefaults.in_isr); +#endif + + printk("* Eviction (%s):\n", scope); + printk(" - Total pages evicted: %lu\n", + stats->eviction.clean + stats->eviction.dirty); + printk(" - Clean pages evicted: %lu\n", + stats->eviction.clean); + printk(" - Dirty pages evicted: %lu\n", + stats->eviction.dirty); +} + void test_touch_anon_pages(void) { unsigned long faults; + struct k_mem_paging_stats_t stats; + k_tid_t tid = k_current_get(); faults = z_num_pagefaults_get(); @@ -70,13 +91,12 @@ void test_touch_anon_pages(void) arena[i] = nums[i % 10]; } - printk("reading data\n"); /* And ensure it can be read back */ + printk("verify written data\n"); for (size_t i = 0; i < arena_size; i++) { zassert_equal(arena[i], nums[i % 10], "arena corrupted at index %d (%p): got 0x%hhx expected 0x%hhx", i, &arena[i], arena[i], nums[i % 10]); - arena[i] = 0; } faults = z_num_pagefaults_get() - faults; @@ -84,6 +104,42 @@ void test_touch_anon_pages(void) /* Specific number depends on how much RAM we have but shouldn't be 0 */ zassert_not_equal(faults, 0UL, "no page faults handled?"); printk("Kernel handled %lu page faults\n", faults); + + k_mem_paging_stats_get(&stats); + print_paging_stats(&stats, "kernel"); + zassert_not_equal(stats.eviction.dirty, 0UL, + "there should be dirty pages being evicted."); + + /* There should be some clean pages to be evicted now, + * since the arena is not modified. + */ + printk("reading unmodified data\n"); + for (size_t i = 0; i < arena_size; i++) { + zassert_equal(arena[i], nums[i % 10], + "arena corrupted at index %d (%p): got 0x%hhx expected 0x%hhx", + i, &arena[i], arena[i], nums[i % 10]); + } + + k_mem_paging_stats_get(&stats); + print_paging_stats(&stats, "kernel"); + zassert_not_equal(stats.eviction.clean, 0UL, + "there should be clean pages being evicted."); + + /* per-thread statistics */ + printk("\nPaging stats for current thread (%p):\n", tid); + k_mem_paging_thread_stats_get(tid, &stats); + print_paging_stats(&stats, "thread"); + zassert_not_equal(stats.pagefaults.cnt, 0UL, + "no page faults handled in thread?"); + zassert_not_equal(stats.eviction.dirty, 0UL, + "test thread should have dirty pages evicted."); + zassert_not_equal(stats.eviction.clean, 0UL, + "test thread should have clean pages evicted."); + + /* Reset arena to zero */ + for (size_t i = 0; i < arena_size; i++) { + arena[i] = 0; + } } void test_k_mem_page_out(void) @@ -217,6 +273,84 @@ void test_backing_store_capacity(void) zassert_not_equal(faults, 0, "should have had some pagefaults"); } +/* Test if we can get paging statistics under usermode */ +void test_user_get_stats(void) +{ + struct k_mem_paging_stats_t stats; + k_tid_t tid = k_current_get(); + + /* overall kernel statistics */ + printk("\nPaging stats for kernel:\n"); + k_mem_paging_stats_get(&stats); + print_paging_stats(&stats, "kernel - usermode"); + zassert_not_equal(stats.pagefaults.cnt, 0UL, + "no page faults handled in thread?"); + zassert_not_equal(stats.eviction.dirty, 0UL, + "test thread should have dirty pages evicted."); + zassert_not_equal(stats.eviction.clean, 0UL, + "test thread should have clean pages evicted."); + + /* per-thread statistics */ + printk("\nPaging stats for current thread (%p):\n", tid); + k_mem_paging_thread_stats_get(tid, &stats); + print_paging_stats(&stats, "thread - usermode"); + zassert_not_equal(stats.pagefaults.cnt, 0UL, + "no page faults handled in thread?"); + zassert_not_equal(stats.eviction.dirty, 0UL, + "test thread should have dirty pages evicted."); + zassert_not_equal(stats.eviction.clean, 0UL, + "test thread should have clean pages evicted."); + +} + +/* Print the histogram and return true if histogram has non-zero values + * in one of its bins. + */ +bool print_histogram(struct k_mem_paging_histogram_t *hist) +{ + bool has_non_zero; + uint64_t time_ns; + int idx; + + has_non_zero = false; + for (idx = 0; + idx < CONFIG_DEMAND_PAGING_TIMING_HISTOGRAM_NUM_BINS; + idx++) { + time_ns = k_cyc_to_ns_ceil64(hist->bounds[idx]); + printk(" <= %llu ns (%lu cycles): %lu\n", time_ns, + hist->bounds[idx], hist->counts[idx]); + if (hist->counts[idx] > 0U) { + has_non_zero = true; + } + } + + return has_non_zero; +} + +/* Test if we can get paging timing histograms */ +void test_user_get_hist(void) +{ + struct k_mem_paging_histogram_t hist; + + printk("Eviction Timing Histogram:\n"); + k_mem_paging_histogram_eviction_get(&hist); + zassert_true(print_histogram(&hist), + "should have non-zero counts in histogram."); + printk("\n"); + + printk("Backing Store Page-IN Histogram:\n"); + k_mem_paging_histogram_backing_store_page_in_get(&hist); + zassert_true(print_histogram(&hist), + "should have non-zero counts in histogram."); + printk("\n"); + + printk("Backing Store Page-OUT Histogram:\n"); + k_mem_paging_histogram_backing_store_page_out_get(&hist); + zassert_true(print_histogram(&hist), + "should have non-zero counts in histogram."); + printk("\n"); +} + /* ztest main entry*/ void test_main(void) { @@ -227,7 +361,9 @@ void test_main(void) ztest_unit_test(test_k_mem_page_in), ztest_unit_test(test_k_mem_pin), ztest_unit_test(test_k_mem_unpin), - ztest_unit_test(test_backing_store_capacity)); + ztest_unit_test(test_backing_store_capacity), + ztest_user_unit_test(test_user_get_stats), + ztest_user_unit_test(test_user_get_hist)); ztest_run_test_suite(test_demand_paging); }