Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2013, 2014 Damien P. George
7 : : *
8 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : * of this software and associated documentation files (the "Software"), to deal
10 : : * in the Software without restriction, including without limitation the rights
11 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : * copies of the Software, and to permit persons to whom the Software is
13 : : * furnished to do so, subject to the following conditions:
14 : : *
15 : : * The above copyright notice and this permission notice shall be included in
16 : : * all copies or substantial portions of the Software.
17 : : *
18 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 : : * THE SOFTWARE.
25 : : */
26 : :
27 : : #include <stdio.h>
28 : : #include <stdlib.h>
29 : : #include <string.h>
30 : :
31 : : #include "py/mpconfig.h"
32 : : #include "py/misc.h"
33 : : #include "py/mpstate.h"
34 : :
35 : : #if MICROPY_DEBUG_VERBOSE // print debugging info
36 : : #define DEBUG_printf DEBUG_printf
37 : : #else // don't print debugging info
38 : : #define DEBUG_printf(...) (void)0
39 : : #endif
40 : :
41 : : #if MICROPY_MEM_STATS
42 : : #if !MICROPY_MALLOC_USES_ALLOCATED_SIZE
43 : : #error MICROPY_MEM_STATS requires MICROPY_MALLOC_USES_ALLOCATED_SIZE
44 : : #endif
45 : : #define UPDATE_PEAK() { if (MP_STATE_MEM(current_bytes_allocated) > MP_STATE_MEM(peak_bytes_allocated)) MP_STATE_MEM(peak_bytes_allocated) = MP_STATE_MEM(current_bytes_allocated); }
46 : : #endif
47 : :
48 : : #if MICROPY_ENABLE_GC
49 : : #include "py/gc.h"
50 : :
51 : : // We redirect standard alloc functions to GC heap - just for the rest of
52 : : // this module. In the rest of MicroPython source, system malloc can be
53 : : // freely accessed - for interfacing with system and 3rd-party libs for
54 : : // example. On the other hand, some (e.g. bare-metal) ports may use GC
55 : : // heap as system heap, so, to avoid warnings, we do undef's first.
56 : : #undef malloc
57 : : #undef free
58 : : #undef realloc
59 : : #define malloc(b) gc_alloc((b), false)
60 : : #define malloc_with_finaliser(b) gc_alloc((b), true)
61 : : #define free gc_free
62 : : #define realloc(ptr, n) gc_realloc(ptr, n, true)
63 : : #define realloc_ext(ptr, n, mv) gc_realloc(ptr, n, mv)
64 : : #else
65 : :
66 : : // GC is disabled. Use system malloc/realloc/free.
67 : :
68 : : #if MICROPY_ENABLE_FINALISER
69 : : #error MICROPY_ENABLE_FINALISER requires MICROPY_ENABLE_GC
70 : : #endif
71 : :
72 : : static void *realloc_ext(void *ptr, size_t n_bytes, bool allow_move) {
73 : : if (allow_move) {
74 : : return realloc(ptr, n_bytes);
75 : : } else {
76 : : // We are asked to resize, but without moving the memory region pointed to
77 : : // by ptr. Unless the underlying memory manager has special provision for
78 : : // this behaviour there is nothing we can do except fail to resize.
79 : : return NULL;
80 : : }
81 : : }
82 : :
83 : : #endif // MICROPY_ENABLE_GC
84 : :
85 : 2991257 : void *m_malloc(size_t num_bytes) {
86 : 2991257 : void *ptr = malloc(num_bytes);
87 [ + + ]: 2991438 : if (ptr == NULL && num_bytes != 0) {
88 : 82 : m_malloc_fail(num_bytes);
89 : : }
90 : : #if MICROPY_MEM_STATS
91 : 2991356 : MP_STATE_MEM(total_bytes_allocated) += num_bytes;
92 : 2991356 : MP_STATE_MEM(current_bytes_allocated) += num_bytes;
93 [ + + ]: 2991356 : UPDATE_PEAK();
94 : : #endif
95 : 2991356 : DEBUG_printf("malloc %d : %p\n", num_bytes, ptr);
96 : 2991356 : return ptr;
97 : : }
98 : :
99 : 901858 : void *m_malloc_maybe(size_t num_bytes) {
100 : 901858 : void *ptr = malloc(num_bytes);
101 : : #if MICROPY_MEM_STATS
102 : 902640 : MP_STATE_MEM(total_bytes_allocated) += num_bytes;
103 : 902640 : MP_STATE_MEM(current_bytes_allocated) += num_bytes;
104 [ + + ]: 902640 : UPDATE_PEAK();
105 : : #endif
106 : 902640 : DEBUG_printf("malloc %d : %p\n", num_bytes, ptr);
107 : 902640 : return ptr;
108 : : }
109 : :
110 : : #if MICROPY_ENABLE_FINALISER
111 : 4459 : void *m_malloc_with_finaliser(size_t num_bytes) {
112 : 4459 : void *ptr = malloc_with_finaliser(num_bytes);
113 [ + + ]: 4459 : if (ptr == NULL && num_bytes != 0) {
114 : 2 : m_malloc_fail(num_bytes);
115 : : }
116 : : #if MICROPY_MEM_STATS
117 : 4457 : MP_STATE_MEM(total_bytes_allocated) += num_bytes;
118 : 4457 : MP_STATE_MEM(current_bytes_allocated) += num_bytes;
119 [ + + ]: 4457 : UPDATE_PEAK();
120 : : #endif
121 : 4457 : DEBUG_printf("malloc %d : %p\n", num_bytes, ptr);
122 : 4457 : return ptr;
123 : : }
124 : : #endif
125 : :
126 : 110573 : void *m_malloc0(size_t num_bytes) {
127 : 110573 : void *ptr = m_malloc(num_bytes);
128 : : // If this config is set then the GC clears all memory, so we don't need to.
129 : : #if !MICROPY_GC_CONSERVATIVE_CLEAR
130 : : memset(ptr, 0, num_bytes);
131 : : #endif
132 : 110571 : return ptr;
133 : : }
134 : :
135 : : #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
136 : 293674 : void *m_realloc(void *ptr, size_t old_num_bytes, size_t new_num_bytes)
137 : : #else
138 : : void *m_realloc(void *ptr, size_t new_num_bytes)
139 : : #endif
140 : : {
141 : 293674 : void *new_ptr = realloc(ptr, new_num_bytes);
142 [ + + ]: 293674 : if (new_ptr == NULL && new_num_bytes != 0) {
143 : 26 : m_malloc_fail(new_num_bytes);
144 : : }
145 : : #if MICROPY_MEM_STATS
146 : : // At first thought, "Total bytes allocated" should only grow,
147 : : // after all, it's *total*. But consider for example 2K block
148 : : // shrunk to 1K and then grown to 2K again. It's still 2K
149 : : // allocated total. If we process only positive increments,
150 : : // we'll count 3K.
151 : 293648 : size_t diff = new_num_bytes - old_num_bytes;
152 : 293648 : MP_STATE_MEM(total_bytes_allocated) += diff;
153 : 293648 : MP_STATE_MEM(current_bytes_allocated) += diff;
154 [ + + ]: 293648 : UPDATE_PEAK();
155 : : #endif
156 : : #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
157 : 293648 : DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr);
158 : : #else
159 : : DEBUG_printf("realloc %p, %d : %p\n", ptr, new_num_bytes, new_ptr);
160 : : #endif
161 : 293648 : return new_ptr;
162 : : }
163 : :
164 : : #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
165 : 243094 : void *m_realloc_maybe(void *ptr, size_t old_num_bytes, size_t new_num_bytes, bool allow_move)
166 : : #else
167 : : void *m_realloc_maybe(void *ptr, size_t new_num_bytes, bool allow_move)
168 : : #endif
169 : : {
170 : 243094 : void *new_ptr = realloc_ext(ptr, new_num_bytes, allow_move);
171 : : #if MICROPY_MEM_STATS
172 : : // At first thought, "Total bytes allocated" should only grow,
173 : : // after all, it's *total*. But consider for example 2K block
174 : : // shrunk to 1K and then grown to 2K again. It's still 2K
175 : : // allocated total. If we process only positive increments,
176 : : // we'll count 3K.
177 : : // Also, don't count failed reallocs.
178 [ + + ]: 243094 : if (!(new_ptr == NULL && new_num_bytes != 0)) {
179 : 230220 : size_t diff = new_num_bytes - old_num_bytes;
180 : 230220 : MP_STATE_MEM(total_bytes_allocated) += diff;
181 : 230220 : MP_STATE_MEM(current_bytes_allocated) += diff;
182 [ + + ]: 230220 : UPDATE_PEAK();
183 : : }
184 : : #endif
185 : : #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
186 : 243094 : DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr);
187 : : #else
188 : : DEBUG_printf("realloc %p, %d : %p\n", ptr, new_num_bytes, new_ptr);
189 : : #endif
190 : 243094 : return new_ptr;
191 : : }
192 : :
193 : : #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
194 : 619455 : void m_free(void *ptr, size_t num_bytes)
195 : : #else
196 : : void m_free(void *ptr)
197 : : #endif
198 : : {
199 : 619455 : free(ptr);
200 : : #if MICROPY_MEM_STATS
201 : 620346 : MP_STATE_MEM(current_bytes_allocated) -= num_bytes;
202 : : #endif
203 : : #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
204 : 620346 : DEBUG_printf("free %p, %d\n", ptr, num_bytes);
205 : : #else
206 : : DEBUG_printf("free %p\n", ptr);
207 : : #endif
208 : 620346 : }
209 : :
210 : : #if MICROPY_TRACKED_ALLOC
211 : :
212 : : #define MICROPY_TRACKED_ALLOC_STORE_SIZE (!MICROPY_ENABLE_GC)
213 : :
214 : : typedef struct _m_tracked_node_t {
215 : : struct _m_tracked_node_t *prev;
216 : : struct _m_tracked_node_t *next;
217 : : #if MICROPY_TRACKED_ALLOC_STORE_SIZE
218 : : uintptr_t size;
219 : : #endif
220 : : uint8_t data[];
221 : : } m_tracked_node_t;
222 : :
223 : : #if MICROPY_DEBUG_VERBOSE
224 : : static size_t m_tracked_count_links(size_t *nb) {
225 : : m_tracked_node_t *node = MP_STATE_VM(m_tracked_head);
226 : : size_t n = 0;
227 : : *nb = 0;
228 : : while (node != NULL) {
229 : : ++n;
230 : : #if MICROPY_TRACKED_ALLOC_STORE_SIZE
231 : : *nb += node->size;
232 : : #else
233 : : *nb += gc_nbytes(node);
234 : : #endif
235 : : node = node->next;
236 : : }
237 : : return n;
238 : : }
239 : : #endif
240 : :
241 : 18 : void *m_tracked_calloc(size_t nmemb, size_t size) {
242 : 18 : m_tracked_node_t *node = m_malloc_maybe(sizeof(m_tracked_node_t) + nmemb * size);
243 [ + - ]: 18 : if (node == NULL) {
244 : : return NULL;
245 : : }
246 : : #if MICROPY_DEBUG_VERBOSE
247 : : size_t nb;
248 : : size_t n = m_tracked_count_links(&nb);
249 : : DEBUG_printf("m_tracked_calloc(%u, %u) -> (%u;%u) %p\n", (int)nmemb, (int)size, (int)n, (int)nb, node);
250 : : #endif
251 [ + + ]: 18 : if (MP_STATE_VM(m_tracked_head) != NULL) {
252 : 14 : MP_STATE_VM(m_tracked_head)->prev = node;
253 : : }
254 : 18 : node->prev = NULL;
255 : 18 : node->next = MP_STATE_VM(m_tracked_head);
256 : 18 : MP_STATE_VM(m_tracked_head) = node;
257 : : #if MICROPY_TRACKED_ALLOC_STORE_SIZE
258 : : node->size = nmemb * size;
259 : : #endif
260 : : #if !MICROPY_GC_CONSERVATIVE_CLEAR
261 : : memset(&node->data[0], 0, nmemb * size);
262 : : #endif
263 : 18 : return &node->data[0];
264 : : }
265 : :
266 : 16 : void m_tracked_free(void *ptr_in) {
267 [ + - ]: 16 : if (ptr_in == NULL) {
268 : : return;
269 : : }
270 : 16 : m_tracked_node_t *node = (m_tracked_node_t *)((uint8_t *)ptr_in - sizeof(m_tracked_node_t));
271 : : #if MICROPY_DEBUG_VERBOSE
272 : : size_t data_bytes;
273 : : #if MICROPY_TRACKED_ALLOC_STORE_SIZE
274 : : data_bytes = node->size;
275 : : #else
276 : : data_bytes = gc_nbytes(node);
277 : : #endif
278 : : size_t nb;
279 : : size_t n = m_tracked_count_links(&nb);
280 : : DEBUG_printf("m_tracked_free(%p, [%p, %p], nbytes=%u, links=%u;%u)\n", node, node->prev, node->next, (int)data_bytes, (int)n, (int)nb);
281 : : #endif
282 [ - + ]: 16 : if (node->next != NULL) {
283 : 0 : node->next->prev = node->prev;
284 : : }
285 [ + + ]: 16 : if (node->prev != NULL) {
286 : 14 : node->prev->next = node->next;
287 : : } else {
288 : 2 : MP_STATE_VM(m_tracked_head) = node->next;
289 : : }
290 : 16 : m_free(node
291 : : #if MICROPY_MALLOC_USES_ALLOCATED_SIZE
292 : : #if MICROPY_TRACKED_ALLOC_STORE_SIZE
293 : : , node->size
294 : : #else
295 : : , gc_nbytes(node)
296 : : #endif
297 : : #endif
298 : : );
299 : : }
300 : :
301 : : #endif // MICROPY_TRACKED_ALLOC
302 : :
303 : : #if MICROPY_MEM_STATS
304 : 34 : size_t m_get_total_bytes_allocated(void) {
305 : 34 : return MP_STATE_MEM(total_bytes_allocated);
306 : : }
307 : :
308 : 26 : size_t m_get_current_bytes_allocated(void) {
309 : 26 : return MP_STATE_MEM(current_bytes_allocated);
310 : : }
311 : :
312 : 26 : size_t m_get_peak_bytes_allocated(void) {
313 : 26 : return MP_STATE_MEM(peak_bytes_allocated);
314 : : }
315 : : #endif
|