1 /*
2  * Copyright (c) 2021 Nordic Semiconductor ASA.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/sys/heap_listener.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/ztest.h>
10 
11 #include <malloc.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stddef.h>
15 
16 /*
17  * Function used by malloc() to obtain or free memory to the system.
18  * Returns the heap end before applying the change.
19  */
20 extern void *sbrk(intptr_t count);
21 
current_heap_end(void)22 static uintptr_t current_heap_end(void)
23 {
24 	return (uintptr_t)sbrk(0);
25 }
26 
27 static ptrdiff_t heap_difference;
28 
heap_resized(uintptr_t heap_id,void * old_heap_end,void * new_heap_end)29 static void heap_resized(uintptr_t heap_id, void *old_heap_end, void *new_heap_end)
30 {
31 	ARG_UNUSED(heap_id);
32 
33 	heap_difference += ((char *)new_heap_end - (char *)old_heap_end);
34 }
35 
36 static HEAP_LISTENER_RESIZE_DEFINE(listener, HEAP_ID_LIBC, heap_resized);
37 
38 void *ptr;
39 
40 /**
41  * @brief Test that heap listener is notified when libc heap size changes.
42  *
43  * This test calls the malloc() and free() followed by malloc_trim() functions
44  * and verifies that the heap listener is notified of allocating or returning
45  * memory from the system.
46  */
ZTEST(newlib_libc_heap_listener,test_alloc_and_trim)47 ZTEST(newlib_libc_heap_listener, test_alloc_and_trim)
48 {
49 	uintptr_t saved_heap_end;
50 
51 	TC_PRINT("Allocating memory...\n");
52 
53 	heap_listener_register(&listener);
54 	saved_heap_end = current_heap_end();
55 	ptr = malloc(4096);
56 
57 	TC_PRINT("Total heap size change: %zi\n", heap_difference);
58 
59 	zassert_true(heap_difference > 0, "Heap increase not detected");
60 	zassert_equal(current_heap_end() - saved_heap_end, heap_difference,
61 		      "Heap increase not detected");
62 
63 	TC_PRINT("Freeing memory...\n");
64 
65 	heap_difference = 0;
66 	saved_heap_end = current_heap_end();
67 	free(ptr);
68 	malloc_trim(0);
69 
70 	/*
71 	 * malloc_trim() may not free any memory to the system if there is not enough to free.
72 	 * Therefore, do not require that heap_difference < 0.
73 	 */
74 	zassert_equal(current_heap_end() - saved_heap_end, heap_difference,
75 		      "Heap decrease not detected");
76 
77 	heap_listener_unregister(&listener);
78 }
79 
80 ZTEST_SUITE(newlib_libc_heap_listener, NULL, NULL, NULL, NULL, NULL);
81