tests: mem_protect/demand_paging: add paging stats tests

This uses the new functions to get paging statistics and test
if they are valid.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
Daniel Leung 2021-03-26 12:42:51 -07:00 committed by Anas Nashif
commit dd239be6ec
2 changed files with 142 additions and 3 deletions

View file

@ -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

View file

@ -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);
}