1 /* 2 * Copyright (c) 2021 Nordic Semiconductor ASA 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 #ifndef ZEPHYR_INCLUDE_SYS_HEAP_LISTENER_H 8 #define ZEPHYR_INCLUDE_SYS_HEAP_LISTENER_H 9 10 #include <stdint.h> 11 #include <zephyr/toolchain.h> 12 #include <zephyr/sys/slist.h> 13 14 #ifdef __cplusplus 15 extern "C" { 16 #endif 17 18 #if defined(CONFIG_HEAP_LISTENER) || defined(__DOXYGEN__) 19 20 /** 21 * @defgroup heap_listener_apis Heap Listener APIs 22 * @ingroup heaps 23 * @{ 24 */ 25 26 enum heap_event_types { 27 /* 28 * Dummy event so an un-initialized but zero-ed listener node 29 * will not trigger any callbacks. 30 */ 31 HEAP_EVT_UNKNOWN = 0, 32 33 HEAP_RESIZE, 34 HEAP_ALLOC, 35 HEAP_FREE, 36 HEAP_REALLOC, 37 38 HEAP_MAX_EVENTS 39 }; 40 41 /** 42 * @typedef heap_listener_resize_cb_t 43 * @brief Callback used when heap is resized 44 * 45 * @note Minimal C library does not emit this event. 46 * 47 * @param heap_id Identifier of heap being resized 48 * @param old_heap_end Pointer to end of heap before resize 49 * @param new_heap_end Pointer to end of heap after resize 50 */ 51 typedef void (*heap_listener_resize_cb_t)(uintptr_t heap_id, 52 void *old_heap_end, 53 void *new_heap_end); 54 55 /** 56 * @typedef heap_listener_alloc_cb_t 57 * @brief Callback used when there is heap allocation 58 * 59 * @note Heaps managed by libraries outside of code in 60 * Zephyr main code repository may not emit this event. 61 * 62 * @note The number of bytes allocated may not match exactly 63 * to the request to the allocation function. Internal 64 * mechanism of the heap may allocate more than 65 * requested. 66 * 67 * @param heap_id Heap identifier 68 * @param mem Pointer to the allocated memory 69 * @param bytes Size of allocated memory 70 */ 71 typedef void (*heap_listener_alloc_cb_t)(uintptr_t heap_id, 72 void *mem, size_t bytes); 73 74 /** 75 * @typedef heap_listener_free_cb_t 76 * @brief Callback used when memory is freed from heap 77 * 78 * @note Heaps managed by libraries outside of code in 79 * Zephyr main code repository may not emit this event. 80 * 81 * @note The number of bytes freed may not match exactly to 82 * the request to the allocation function. Internal 83 * mechanism of the heap dictates how memory is 84 * allocated or freed. 85 * 86 * @param heap_id Heap identifier 87 * @param mem Pointer to the freed memory 88 * @param bytes Size of freed memory 89 */ 90 typedef void (*heap_listener_free_cb_t)(uintptr_t heap_id, 91 void *mem, size_t bytes); 92 93 struct heap_listener { 94 /** Singly linked list node */ 95 sys_snode_t node; 96 97 /** 98 * Identifier of the heap whose events are listened. 99 * 100 * It can be a heap pointer, if the heap is represented as an object, 101 * or 0 in the case of the global libc heap. 102 */ 103 uintptr_t heap_id; 104 105 /** 106 * The heap event to be notified. 107 */ 108 enum heap_event_types event; 109 110 union { 111 heap_listener_alloc_cb_t alloc_cb; 112 heap_listener_free_cb_t free_cb; 113 heap_listener_resize_cb_t resize_cb; 114 }; 115 }; 116 117 /** 118 * @brief Register heap event listener 119 * 120 * Add the listener to the global list of heap listeners that can be notified by 121 * different heap implementations upon certain events related to the heap usage. 122 * 123 * @param listener Pointer to the heap_listener object 124 */ 125 void heap_listener_register(struct heap_listener *listener); 126 127 /** 128 * @brief Unregister heap event listener 129 * 130 * Remove the listener from the global list of heap listeners that can be 131 * notified by different heap implementations upon certain events related to the 132 * heap usage. 133 * 134 * @param listener Pointer to the heap_listener object 135 */ 136 void heap_listener_unregister(struct heap_listener *listener); 137 138 /** 139 * @brief Notify listeners of heap allocation event 140 * 141 * Notify registered heap event listeners with matching heap identifier that an 142 * allocation has been done on heap 143 * 144 * @param heap_id Heap identifier 145 * @param mem Pointer to the allocated memory 146 * @param bytes Size of allocated memory 147 */ 148 void heap_listener_notify_alloc(uintptr_t heap_id, void *mem, size_t bytes); 149 150 /** 151 * @brief Notify listeners of heap free event 152 * 153 * Notify registered heap event listeners with matching heap identifier that 154 * memory is freed on heap 155 * 156 * @param heap_id Heap identifier 157 * @param mem Pointer to the freed memory 158 * @param bytes Size of freed memory 159 */ 160 void heap_listener_notify_free(uintptr_t heap_id, void *mem, size_t bytes); 161 162 /** 163 * @brief Notify listeners of heap resize event 164 * 165 * Notify registered heap event listeners with matching heap identifier that the 166 * heap has been resized. 167 * 168 * @param heap_id Heap identifier 169 * @param old_heap_end Address of the heap end before the change 170 * @param new_heap_end Address of the heap end after the change 171 */ 172 void heap_listener_notify_resize(uintptr_t heap_id, void *old_heap_end, void *new_heap_end); 173 174 /** 175 * @brief Construct heap identifier from heap pointer 176 * 177 * Construct a heap identifier from a pointer to the heap object, such as 178 * sys_heap. 179 * 180 * @param heap_pointer Pointer to the heap object 181 */ 182 #define HEAP_ID_FROM_POINTER(heap_pointer) ((uintptr_t)heap_pointer) 183 184 /** 185 * @brief Libc heap identifier 186 * 187 * Identifier of the global libc heap. 188 */ 189 #define HEAP_ID_LIBC ((uintptr_t)0) 190 191 /** 192 * @brief Define heap event listener node for allocation event 193 * 194 * Sample usage: 195 * @code 196 * void on_heap_alloc(uintptr_t heap_id, void *mem, size_t bytes) 197 * { 198 * LOG_INF("Memory allocated at %p, size %ld", mem, bytes); 199 * } 200 * 201 * HEAP_LISTENER_ALLOC_DEFINE(my_listener, HEAP_ID_LIBC, on_heap_alloc); 202 * @endcode 203 * 204 * @param name Name of the heap event listener object 205 * @param _heap_id Identifier of the heap to be listened 206 * @param _alloc_cb Function to be called for allocation event 207 */ 208 #define HEAP_LISTENER_ALLOC_DEFINE(name, _heap_id, _alloc_cb) \ 209 struct heap_listener name = { \ 210 .heap_id = _heap_id, \ 211 .event = HEAP_ALLOC, \ 212 { \ 213 .alloc_cb = _alloc_cb \ 214 }, \ 215 } 216 217 /** 218 * @brief Define heap event listener node for free event 219 * 220 * Sample usage: 221 * @code 222 * void on_heap_free(uintptr_t heap_id, void *mem, size_t bytes) 223 * { 224 * LOG_INF("Memory freed at %p, size %ld", mem, bytes); 225 * } 226 * 227 * HEAP_LISTENER_FREE_DEFINE(my_listener, HEAP_ID_LIBC, on_heap_free); 228 * @endcode 229 * 230 * @param name Name of the heap event listener object 231 * @param _heap_id Identifier of the heap to be listened 232 * @param _free_cb Function to be called for free event 233 */ 234 #define HEAP_LISTENER_FREE_DEFINE(name, _heap_id, _free_cb) \ 235 struct heap_listener name = { \ 236 .heap_id = _heap_id, \ 237 .event = HEAP_FREE, \ 238 { \ 239 .free_cb = _free_cb \ 240 }, \ 241 } 242 243 /** 244 * @brief Define heap event listener node for resize event 245 * 246 * Sample usage: 247 * @code 248 * void on_heap_resized(uintptr_t heap_id, void *old_heap_end, void *new_heap_end) 249 * { 250 * LOG_INF("Libc heap end moved from %p to %p", old_heap_end, new_heap_end); 251 * } 252 * 253 * HEAP_LISTENER_RESIZE_DEFINE(my_listener, HEAP_ID_LIBC, on_heap_resized); 254 * @endcode 255 * 256 * @param name Name of the heap event listener object 257 * @param _heap_id Identifier of the heap to be listened 258 * @param _resize_cb Function to be called when the listened heap is resized 259 */ 260 #define HEAP_LISTENER_RESIZE_DEFINE(name, _heap_id, _resize_cb) \ 261 struct heap_listener name = { \ 262 .heap_id = _heap_id, \ 263 .event = HEAP_RESIZE, \ 264 { \ 265 .resize_cb = _resize_cb \ 266 }, \ 267 } 268 269 /** @} */ 270 271 #else /* CONFIG_HEAP_LISTENER */ 272 273 #define HEAP_ID_FROM_POINTER(heap_pointer) ((uintptr_t)NULL) 274 275 static inline void heap_listener_notify_alloc(uintptr_t heap_id, void *mem, size_t bytes) 276 { 277 ARG_UNUSED(heap_id); 278 ARG_UNUSED(mem); 279 ARG_UNUSED(bytes); 280 } 281 282 static inline void heap_listener_notify_free(uintptr_t heap_id, void *mem, size_t bytes) 283 { 284 ARG_UNUSED(heap_id); 285 ARG_UNUSED(mem); 286 ARG_UNUSED(bytes); 287 } 288 289 static inline void heap_listener_notify_resize(uintptr_t heap_id, void *old_heap_end, 290 void *new_heap_end) 291 { 292 ARG_UNUSED(heap_id); 293 ARG_UNUSED(old_heap_end); 294 ARG_UNUSED(new_heap_end); 295 } 296 297 #endif /* CONFIG_HEAP_LISTENER */ 298 299 #ifdef __cplusplus 300 } 301 #endif 302 303 #endif /* ZEPHYR_INCLUDE_SYS_HEAP_LISTENER_H */ 304