diff options
author | Vladimir Mezentsev <vladimir.mezentsev@oracle.com> | 2022-03-11 08:58:31 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2022-03-11 08:58:31 +0000 |
commit | bb368aad297fe3ad40cf397e6fc85aa471429a28 (patch) | |
tree | 0ab25909b8fe789d676bbdb00d501d4d485e4afe /gprofng/libcollector/memmgr.c | |
parent | a655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff) | |
download | gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.zip gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.gz gdb-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.bz2 |
gprofng: a new GNU profiler
top-level
* Makefile.def: Add gprofng module.
* configure.ac: Add --enable-gprofng option.
* src-release.sh: Add gprofng.
* Makefile.in: Regenerate.
* configure: Regenerate.
* gprofng: New directory.
binutils
* MAINTAINERS: Add gprofng maintainer.
* README-how-to-make-a-release: Add gprofng.
include.
* collectorAPI.h: New file.
* libcollector.h: New file.
* libfcollector.h: New file.
Diffstat (limited to 'gprofng/libcollector/memmgr.c')
-rw-r--r-- | gprofng/libcollector/memmgr.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/gprofng/libcollector/memmgr.c b/gprofng/libcollector/memmgr.c new file mode 100644 index 0000000..5ada421 --- /dev/null +++ b/gprofng/libcollector/memmgr.c @@ -0,0 +1,396 @@ +/* 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; +} |