memmgr.c 11 KB

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