]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gprofng/libcollector/memmgr.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / gprofng / libcollector / memmgr.c
1 /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
2 Contributed by Oracle.
3
4 This file is part of GNU Binutils.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, 51 Franklin Street - Fifth Floor, Boston,
19 MA 02110-1301, USA. */
20
21 #include "config.h"
22 #include <sys/mman.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27
28 #include "collector.h"
29 #include "libcol_util.h"
30 #include "gp-experiment.h"
31 #include "memmgr.h"
32
33 /* TprintfT(<level>,...) definitions. Adjust per module as needed */
34 #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
35 #define DBG_LT1 1 // for configuration details, warnings
36 #define DBG_LT2 2
37 #define DBG_LT3 3
38 #define DBG_LT4 4
39
40 /*
41 * Memory allocation.
42 *
43 * Heap:
44 * chain[0] - linked list of chunks;
45 * chain[1] - linked list of free 16-byte objects;
46 * chain[2] - linked list of free 32-byte objects;
47 * ...
48 *
49 * Chunk:
50 *
51 * base lo hi
52 * V V V
53 * +------------------+---------+-------------------+--+--+-----+
54 * | Var size object | -> <-| Const size objects| | |Chunk|
55 * +------------------+---------+-------------------+--+--+-----+
56 *
57 * Limitations:
58 * - one var size object per chunk
59 * - can't allocate const size objects larger than 2^MAXCHAIN
60 */
61
62 #define MAXCHAIN 32
63 #define ALIGNMENT 4 /* 2^ALIGNMENT == minimal size and alignment */
64 #define ALIGN(x) ((((x) - 1)/(1 << ALIGNMENT) + 1) * (1 << ALIGNMENT))
65
66 struct Heap
67 {
68 collector_mutex_t lock; /* master lock */
69 void *chain[MAXCHAIN]; /* chain[0] - chunks */
70 /* chain[i] - structs of size 2^i */
71 };
72
73 typedef struct Chunk
74 {
75 size_t size;
76 char *base;
77 char *lo;
78 char *hi;
79 struct Chunk *next;
80 } Chunk;
81
82 static void
83 not_implemented ()
84 {
85 __collector_log_write ("<event kind=\"%s\" id=\"%d\">error memmgr not_implemented()</event>\n",
86 SP_JCMD_CERROR, COL_ERROR_NOZMEM);
87 return;
88 }
89
90 /*
91 * void __collector_mmgr_init_mutex_locks( Heap *heap )
92 * Iinitialize mmgr mutex locks.
93 */
94 void
95 __collector_mmgr_init_mutex_locks (Heap *heap)
96 {
97 if (heap == NULL)
98 return;
99 if (__collector_mutex_trylock (&heap->lock))
100 {
101 /*
102 * We are in a child process immediately after the fork().
103 * Parent process was in the middle of critical section when the fork() happened.
104 * This is a placeholder for the cleanup.
105 * See CR 6997020 for details.
106 */
107 __collector_mutex_init (&heap->lock);
108 }
109 __collector_mutex_init (&heap->lock);
110 }
111
112 /*
113 * alloc_chunk( unsigned sz ) allocates a chunk of at least sz bytes.
114 * If sz == 0, allocates a chunk of the default size.
115 */
116 static Chunk *
117 alloc_chunk (unsigned sz, int log)
118 {
119 static long pgsz = 0;
120 char *ptr;
121 Chunk *chnk;
122 size_t chunksz;
123 if (pgsz == 0)
124 {
125 pgsz = CALL_UTIL (sysconf)(_SC_PAGESIZE);
126 Tprintf (DBG_LT2, "memmgr: pgsz = %ld (0x%lx)\n", pgsz, pgsz);
127 }
128 /* Allocate 2^n >= sz bytes */
129 unsigned nsz = ALIGN (sizeof (Chunk)) + sz;
130 for (chunksz = pgsz; chunksz < nsz; chunksz *= 2);
131 if (log == 1)
132 Tprintf (DBG_LT2, "alloc_chunk mapping %u, rounded up from %u\n", (unsigned int) chunksz, sz);
133 /* mmap64 is only in 32-bits; this call goes to mmap in 64-bits */
134 ptr = (char*) CALL_UTIL (mmap64_)(0, chunksz, PROT_READ | PROT_WRITE,
135 MAP_PRIVATE | MAP_ANON, (int) -1, (off64_t) 0);
136 if (ptr == MAP_FAILED)
137 {
138 Tprintf (0, "alloc_chunk mapping failed COL_ERROR_NOZMEMMAP: %s\n", CALL_UTIL (strerror)(errno));
139 __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n",
140 SP_JCMD_CERROR, COL_ERROR_NOZMEMMAP, errno, "0");
141 return NULL;
142 }
143 /* Put the chunk descriptor at the end of the chunk */
144 chnk = (Chunk*) (ptr + chunksz - ALIGN (sizeof (Chunk)));
145 chnk->size = chunksz;
146 chnk->base = ptr;
147 chnk->lo = chnk->base;
148 chnk->hi = (char*) chnk;
149 chnk->next = (Chunk*) NULL;
150 if (log == 1)
151 Tprintf (DBG_LT2, "memmgr: returning new chunk @%p, chunksx=%ld sz=%ld\n",
152 ptr, (long) chunksz, (long) sz);
153 return chnk;
154 }
155
156 Heap *
157 __collector_newHeap ()
158 {
159 Heap *heap;
160 Chunk *chnk;
161 Tprintf (DBG_LT2, "__collector_newHeap calling alloc_chunk(0)\n");
162 chnk = alloc_chunk (0, 1);
163 if (chnk == NULL)
164 return NULL;
165
166 /* A bit of hackery: allocate heap from its own chunk */
167 chnk->hi -= ALIGN (sizeof (Heap));
168 heap = (Heap*) chnk->hi;
169 heap->chain[0] = (void*) chnk;
170 __collector_mutex_init (&heap->lock);
171 return heap;
172 }
173
174 void
175 __collector_deleteHeap (Heap *heap)
176 {
177 if (heap == NULL)
178 return;
179 /* Note: heap itself is in the last chunk */
180 for (Chunk *chnk = heap->chain[0]; chnk;)
181 {
182 Chunk *next = chnk->next;
183 CALL_UTIL (munmap)((void*) chnk->base, chnk->size);
184 chnk = next;
185 }
186 }
187
188 void *
189 __collector_allocCSize (Heap *heap, unsigned sz, int log)
190 {
191 void *res;
192 Chunk *chnk;
193 if (heap == NULL)
194 return NULL;
195
196 /* block all signals and acquire lock */
197 sigset_t old_mask, new_mask;
198 CALL_UTIL (sigfillset)(&new_mask);
199 CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask);
200 __collector_mutex_lock (&heap->lock);
201
202 /* Allocate nsz = 2^idx >= sz bytes */
203 unsigned idx = ALIGNMENT;
204 unsigned nsz = 1 << idx;
205 while (nsz < sz)
206 nsz = 1 << ++idx;
207
208 /* Look in the corresponding chain first */
209 if (idx < MAXCHAIN)
210 {
211 if (heap->chain[idx] != NULL)
212 {
213 res = heap->chain[idx];
214 heap->chain[idx] = *(void**) res;
215 __collector_mutex_unlock (&heap->lock);
216 CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
217 if (log == 1)
218 Tprintf (DBG_LT2, "memmgr: allocCSize %p sz %d (0x%x) req = 0x%x, from chain idx = %d\n", res, nsz, nsz, sz, idx);
219 return res;
220 }
221 }
222 else
223 {
224 not_implemented ();
225 __collector_mutex_unlock (&heap->lock);
226 CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
227 return NULL;
228 }
229
230 /* Chain is empty, allocate from chunks */
231 for (chnk = (Chunk*) heap->chain[0]; chnk; chnk = chnk->next)
232 if (chnk->lo + nsz < chnk->hi)
233 break;
234 if (chnk == NULL)
235 {
236 /* Get a new chunk */
237 if (log == 1)
238 Tprintf (DBG_LT2, "__collector_allocCSize (%u) calling alloc_chunk(%u)\n", sz, nsz);
239 chnk = alloc_chunk (nsz, 1);
240 if (chnk == NULL)
241 {
242 __collector_mutex_unlock (&heap->lock);
243 CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
244 return NULL;
245 }
246 chnk->next = (Chunk*) heap->chain[0];
247 heap->chain[0] = chnk;
248 }
249
250 /* Allocate from the chunk */
251 chnk->hi -= nsz;
252 res = (void*) chnk->hi;
253 __collector_mutex_unlock (&heap->lock);
254 CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
255 if (log == 1)
256 Tprintf (DBG_LT2, "memmgr: allocCSize %p sz %d (0x%x) req = 0x%x, new chunk\n", res, nsz, nsz, sz);
257 return res;
258 }
259
260 void
261 __collector_freeCSize (Heap *heap, void *ptr, unsigned sz)
262 {
263 if (heap == NULL || ptr == NULL)
264 return;
265
266 /* block all signals and acquire lock */
267 sigset_t old_mask, new_mask;
268 CALL_UTIL (sigfillset)(&new_mask);
269 CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask);
270 __collector_mutex_lock (&heap->lock);
271
272 /* Free 2^idx >= sz bytes */
273 unsigned idx = ALIGNMENT;
274 unsigned nsz = 1 << idx;
275 while (nsz < sz)
276 nsz = 1 << ++idx;
277 if (idx < MAXCHAIN)
278 {
279 *(void**) ptr = heap->chain[idx];
280 heap->chain[idx] = ptr;
281 }
282 else
283 not_implemented ();
284 __collector_mutex_unlock (&heap->lock);
285 CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
286 Tprintf (DBG_LT4, "memmgr: freeC %p sz %ld\n", ptr, (long) sz);
287 }
288
289 static void *
290 allocVSize_nolock (Heap *heap, unsigned sz)
291 {
292 void *res;
293 Chunk *chnk;
294 if (sz == 0)
295 return NULL;
296
297 /* Find a good chunk */
298 for (chnk = (Chunk*) heap->chain[0]; chnk; chnk = chnk->next)
299 if (chnk->lo == chnk->base && chnk->lo + sz < chnk->hi)
300 break;
301 if (chnk == NULL)
302 {
303 /* Get a new chunk */
304 Tprintf (DBG_LT2, "allocVsize_nolock calling alloc_chunk(%u)\n", sz);
305 chnk = alloc_chunk (sz, 0);
306 if (chnk == NULL)
307 return NULL;
308 chnk->next = (Chunk*) heap->chain[0];
309 heap->chain[0] = chnk;
310 }
311 chnk->lo = chnk->base + sz;
312 res = (void*) (chnk->base);
313 Tprintf (DBG_LT4, "memmgr: allocV %p for %ld\n", res, (long) sz);
314 return res;
315 }
316
317 void *
318 __collector_allocVSize (Heap *heap, unsigned sz)
319 {
320 void *res;
321 if (heap == NULL)
322 return NULL;
323
324 /* block all signals and acquire lock */
325 sigset_t old_mask, new_mask;
326 CALL_UTIL (sigfillset)(&new_mask);
327 CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask);
328 __collector_mutex_lock (&heap->lock);
329 res = allocVSize_nolock (heap, sz);
330 __collector_mutex_unlock (&heap->lock);
331 CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
332 return res;
333 }
334
335 /*
336 * reallocVSize( Heap *heap, void *ptr, unsigned newsz )
337 * Changes the size of memory pointed by ptr to newsz.
338 * If ptr == NULL, allocates new memory of size newsz.
339 * If newsz == 0, frees ptr and returns NULL.
340 */
341 void *
342 __collector_reallocVSize (Heap *heap, void *ptr, unsigned newsz)
343 {
344 Chunk *chnk;
345 void *res;
346 if (heap == NULL)
347 return NULL;
348 if (ptr == NULL)
349 return __collector_allocVSize (heap, newsz);
350
351 /* block all signals and acquire lock */
352 sigset_t old_mask, new_mask;
353 CALL_UTIL (sigfillset)(&new_mask);
354 CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask);
355 __collector_mutex_lock (&heap->lock);
356
357 /* Find its chunk */
358 for (chnk = (Chunk*) heap->chain[0]; chnk; chnk = chnk->next)
359 if (ptr == chnk->base)
360 break;
361 if (chnk == NULL)
362 {
363 /* memory corrpution */
364 not_implemented ();
365 __collector_mutex_unlock (&heap->lock);
366 CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
367 return NULL;
368 }
369 if (chnk->base + newsz < chnk->hi)
370 {
371 /* easy case */
372 chnk->lo = chnk->base + newsz;
373 res = newsz ? chnk->base : NULL;
374 __collector_mutex_unlock (&heap->lock);
375 CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
376 Tprintf (DBG_LT4, "memmgr: reallocV %p for %ld\n", ptr, (long) newsz);
377 return res;
378 }
379 res = allocVSize_nolock (heap, newsz);
380 /* Copy to new location */
381 if (res)
382 {
383 int size = chnk->lo - chnk->base;
384 if (newsz < size)
385 size = newsz;
386 char *s1 = (char*) res;
387 char *s2 = chnk->base;
388 while (size--)
389 *s1++ = *s2++;
390 }
391 /* Free old memory*/
392 chnk->lo = chnk->base;
393 __collector_mutex_unlock (&heap->lock);
394 CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
395 return res;
396 }