123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- /* Copyright (C) 2021 Free Software Foundation, Inc.
- Contributed by Oracle.
- This file is part of GNU Binutils.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, 51 Franklin Street - Fifth Floor, Boston,
- MA 02110-1301, USA. */
- #include "config.h"
- #include <sys/mman.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include "collector.h"
- #include "libcol_util.h"
- #include "gp-experiment.h"
- #include "memmgr.h"
- /* TprintfT(<level>,...) definitions. Adjust per module as needed */
- #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
- #define DBG_LT1 1 // for configuration details, warnings
- #define DBG_LT2 2
- #define DBG_LT3 3
- #define DBG_LT4 4
- /*
- * Memory allocation.
- *
- * Heap:
- * chain[0] - linked list of chunks;
- * chain[1] - linked list of free 16-byte objects;
- * chain[2] - linked list of free 32-byte objects;
- * ...
- *
- * Chunk:
- *
- * base lo hi
- * V V V
- * +------------------+---------+-------------------+--+--+-----+
- * | Var size object | -> <-| Const size objects| | |Chunk|
- * +------------------+---------+-------------------+--+--+-----+
- *
- * Limitations:
- * - one var size object per chunk
- * - can't allocate const size objects larger than 2^MAXCHAIN
- */
- #define MAXCHAIN 32
- #define ALIGNMENT 4 /* 2^ALIGNMENT == minimal size and alignment */
- #define ALIGN(x) ((((x) - 1)/(1 << ALIGNMENT) + 1) * (1 << ALIGNMENT))
- struct Heap
- {
- collector_mutex_t lock; /* master lock */
- void *chain[MAXCHAIN]; /* chain[0] - chunks */
- /* chain[i] - structs of size 2^i */
- };
- typedef struct Chunk
- {
- size_t size;
- char *base;
- char *lo;
- char *hi;
- struct Chunk *next;
- } Chunk;
- static void
- not_implemented ()
- {
- __collector_log_write ("<event kind=\"%s\" id=\"%d\">error memmgr not_implemented()</event>\n",
- SP_JCMD_CERROR, COL_ERROR_NOZMEM);
- return;
- }
- /*
- * void __collector_mmgr_init_mutex_locks( Heap *heap )
- * Iinitialize mmgr mutex locks.
- */
- void
- __collector_mmgr_init_mutex_locks (Heap *heap)
- {
- if (heap == NULL)
- return;
- if (__collector_mutex_trylock (&heap->lock))
- {
- /*
- * We are in a child process immediately after the fork().
- * Parent process was in the middle of critical section when the fork() happened.
- * This is a placeholder for the cleanup.
- * See CR 6997020 for details.
- */
- __collector_mutex_init (&heap->lock);
- }
- __collector_mutex_init (&heap->lock);
- }
- /*
- * alloc_chunk( unsigned sz ) allocates a chunk of at least sz bytes.
- * If sz == 0, allocates a chunk of the default size.
- */
- static Chunk *
- alloc_chunk (unsigned sz, int log)
- {
- static long pgsz = 0;
- char *ptr;
- Chunk *chnk;
- size_t chunksz;
- if (pgsz == 0)
- {
- pgsz = CALL_UTIL (sysconf)(_SC_PAGESIZE);
- Tprintf (DBG_LT2, "memmgr: pgsz = %ld (0x%lx)\n", pgsz, pgsz);
- }
- /* Allocate 2^n >= sz bytes */
- unsigned nsz = ALIGN (sizeof (Chunk)) + sz;
- for (chunksz = pgsz; chunksz < nsz; chunksz *= 2);
- if (log == 1)
- Tprintf (DBG_LT2, "alloc_chunk mapping %u, rounded up from %u\n", (unsigned int) chunksz, sz);
- /* mmap64 is only in 32-bits; this call goes to mmap in 64-bits */
- ptr = (char*) CALL_UTIL (mmap64)(0, chunksz, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON, (int) -1, (off64_t) 0);
- if (ptr == MAP_FAILED)
- {
- Tprintf (0, "alloc_chunk mapping failed COL_ERROR_NOZMEMMAP: %s\n", CALL_UTIL (strerror)(errno));
- __collector_log_write ("<event kind=\"%s\" id=\"%d\" ec=\"%d\">%s</event>\n",
- SP_JCMD_CERROR, COL_ERROR_NOZMEMMAP, errno, "0");
- return NULL;
- }
- /* Put the chunk descriptor at the end of the chunk */
- chnk = (Chunk*) (ptr + chunksz - ALIGN (sizeof (Chunk)));
- chnk->size = chunksz;
- chnk->base = ptr;
- chnk->lo = chnk->base;
- chnk->hi = (char*) chnk;
- chnk->next = (Chunk*) NULL;
- if (log == 1)
- Tprintf (DBG_LT2, "memmgr: returning new chunk @%p, chunksx=%ld sz=%ld\n",
- ptr, (long) chunksz, (long) sz);
- return chnk;
- }
- Heap *
- __collector_newHeap ()
- {
- Heap *heap;
- Chunk *chnk;
- Tprintf (DBG_LT2, "__collector_newHeap calling alloc_chunk(0)\n");
- chnk = alloc_chunk (0, 1);
- if (chnk == NULL)
- return NULL;
- /* A bit of hackery: allocate heap from its own chunk */
- chnk->hi -= ALIGN (sizeof (Heap));
- heap = (Heap*) chnk->hi;
- heap->chain[0] = (void*) chnk;
- __collector_mutex_init (&heap->lock);
- return heap;
- }
- void
- __collector_deleteHeap (Heap *heap)
- {
- if (heap == NULL)
- return;
- /* Note: heap itself is in the last chunk */
- for (Chunk *chnk = heap->chain[0]; chnk;)
- {
- Chunk *next = chnk->next;
- CALL_UTIL (munmap)((void*) chnk->base, chnk->size);
- chnk = next;
- }
- }
- void *
- __collector_allocCSize (Heap *heap, unsigned sz, int log)
- {
- void *res;
- Chunk *chnk;
- if (heap == NULL)
- return NULL;
- /* block all signals and acquire lock */
- sigset_t old_mask, new_mask;
- CALL_UTIL (sigfillset)(&new_mask);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask);
- __collector_mutex_lock (&heap->lock);
- /* Allocate nsz = 2^idx >= sz bytes */
- unsigned idx = ALIGNMENT;
- unsigned nsz = 1 << idx;
- while (nsz < sz)
- nsz = 1 << ++idx;
- /* Look in the corresponding chain first */
- if (idx < MAXCHAIN)
- {
- if (heap->chain[idx] != NULL)
- {
- res = heap->chain[idx];
- heap->chain[idx] = *(void**) res;
- __collector_mutex_unlock (&heap->lock);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
- if (log == 1)
- Tprintf (DBG_LT2, "memmgr: allocCSize %p sz %d (0x%x) req = 0x%x, from chain idx = %d\n", res, nsz, nsz, sz, idx);
- return res;
- }
- }
- else
- {
- not_implemented ();
- __collector_mutex_unlock (&heap->lock);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
- return NULL;
- }
- /* Chain is empty, allocate from chunks */
- for (chnk = (Chunk*) heap->chain[0]; chnk; chnk = chnk->next)
- if (chnk->lo + nsz < chnk->hi)
- break;
- if (chnk == NULL)
- {
- /* Get a new chunk */
- if (log == 1)
- Tprintf (DBG_LT2, "__collector_allocCSize (%u) calling alloc_chunk(%u)\n", sz, nsz);
- chnk = alloc_chunk (nsz, 1);
- if (chnk == NULL)
- {
- __collector_mutex_unlock (&heap->lock);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
- return NULL;
- }
- chnk->next = (Chunk*) heap->chain[0];
- heap->chain[0] = chnk;
- }
- /* Allocate from the chunk */
- chnk->hi -= nsz;
- res = (void*) chnk->hi;
- __collector_mutex_unlock (&heap->lock);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
- if (log == 1)
- Tprintf (DBG_LT2, "memmgr: allocCSize %p sz %d (0x%x) req = 0x%x, new chunk\n", res, nsz, nsz, sz);
- return res;
- }
- void
- __collector_freeCSize (Heap *heap, void *ptr, unsigned sz)
- {
- if (heap == NULL || ptr == NULL)
- return;
- /* block all signals and acquire lock */
- sigset_t old_mask, new_mask;
- CALL_UTIL (sigfillset)(&new_mask);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask);
- __collector_mutex_lock (&heap->lock);
- /* Free 2^idx >= sz bytes */
- unsigned idx = ALIGNMENT;
- unsigned nsz = 1 << idx;
- while (nsz < sz)
- nsz = 1 << ++idx;
- if (idx < MAXCHAIN)
- {
- *(void**) ptr = heap->chain[idx];
- heap->chain[idx] = ptr;
- }
- else
- not_implemented ();
- __collector_mutex_unlock (&heap->lock);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
- Tprintf (DBG_LT4, "memmgr: freeC %p sz %ld\n", ptr, (long) sz);
- }
- static void *
- allocVSize_nolock (Heap *heap, unsigned sz)
- {
- void *res;
- Chunk *chnk;
- if (sz == 0)
- return NULL;
- /* Find a good chunk */
- for (chnk = (Chunk*) heap->chain[0]; chnk; chnk = chnk->next)
- if (chnk->lo == chnk->base && chnk->lo + sz < chnk->hi)
- break;
- if (chnk == NULL)
- {
- /* Get a new chunk */
- Tprintf (DBG_LT2, "allocVsize_nolock calling alloc_chunk(%u)\n", sz);
- chnk = alloc_chunk (sz, 0);
- if (chnk == NULL)
- return NULL;
- chnk->next = (Chunk*) heap->chain[0];
- heap->chain[0] = chnk;
- }
- chnk->lo = chnk->base + sz;
- res = (void*) (chnk->base);
- Tprintf (DBG_LT4, "memmgr: allocV %p for %ld\n", res, (long) sz);
- return res;
- }
- void *
- __collector_allocVSize (Heap *heap, unsigned sz)
- {
- void *res;
- if (heap == NULL)
- return NULL;
- /* block all signals and acquire lock */
- sigset_t old_mask, new_mask;
- CALL_UTIL (sigfillset)(&new_mask);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask);
- __collector_mutex_lock (&heap->lock);
- res = allocVSize_nolock (heap, sz);
- __collector_mutex_unlock (&heap->lock);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
- return res;
- }
- /*
- * reallocVSize( Heap *heap, void *ptr, unsigned newsz )
- * Changes the size of memory pointed by ptr to newsz.
- * If ptr == NULL, allocates new memory of size newsz.
- * If newsz == 0, frees ptr and returns NULL.
- */
- void *
- __collector_reallocVSize (Heap *heap, void *ptr, unsigned newsz)
- {
- Chunk *chnk;
- void *res;
- if (heap == NULL)
- return NULL;
- if (ptr == NULL)
- return __collector_allocVSize (heap, newsz);
- /* block all signals and acquire lock */
- sigset_t old_mask, new_mask;
- CALL_UTIL (sigfillset)(&new_mask);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &new_mask, &old_mask);
- __collector_mutex_lock (&heap->lock);
- /* Find its chunk */
- for (chnk = (Chunk*) heap->chain[0]; chnk; chnk = chnk->next)
- if (ptr == chnk->base)
- break;
- if (chnk == NULL)
- {
- /* memory corrpution */
- not_implemented ();
- __collector_mutex_unlock (&heap->lock);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
- return NULL;
- }
- if (chnk->base + newsz < chnk->hi)
- {
- /* easy case */
- chnk->lo = chnk->base + newsz;
- res = newsz ? chnk->base : NULL;
- __collector_mutex_unlock (&heap->lock);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
- Tprintf (DBG_LT4, "memmgr: reallocV %p for %ld\n", ptr, (long) newsz);
- return res;
- }
- res = allocVSize_nolock (heap, newsz);
- /* Copy to new location */
- if (res)
- {
- int size = chnk->lo - chnk->base;
- if (newsz < size)
- size = newsz;
- char *s1 = (char*) res;
- char *s2 = chnk->base;
- while (size--)
- *s1++ = *s2++;
- }
- /* Free old memory*/
- chnk->lo = chnk->base;
- __collector_mutex_unlock (&heap->lock);
- CALL_UTIL (sigprocmask)(SIG_SETMASK, &old_mask, NULL);
- return res;
- }
|