Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
fix MC on FreeBSD
[simgrid.git] / src / xbt / mmalloc / mm_module.c
1 /* Initialization for acces s to a mmap'd malloc managed region. */
2
3 /* Copyright (c) 2012-2019. The SimGrid Team.
4  * All rights reserved.                                                     */
5
6 /* This program is free software; you can redistribute it and/or modify it
7  * under the terms of the license (GNU LGPL) which comes with this package. */
8
9 /* Copyright 1992, 2000 Free Software Foundation, Inc.
10
11    Contributed by Fred Fish at Cygnus Support.   fnf@cygnus.com
12
13    This file is part of the GNU C Library.
14
15    The GNU C Library is free software; you can redistribute it and/or
16    modify it under the terms of the GNU Library General Public License as
17    published by the Free Software Foundation; either version 2 of the
18    License, or (at your option) any later version.
19
20    The GNU C Library is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    Library General Public License for more details.
24
25    You should have received a copy of the GNU Library General Public
26    License along with the GNU C Library; see the file COPYING.LIB.  If
27    not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
28    Boston, MA 02111-1307, USA.  */
29
30 #include "src/internal_config.h"
31 #include <sys/types.h>
32 #include <fcntl.h>              /* After sys/types.h, at least for dpx/2.  */
33 #include <sys/stat.h>
34 #include <string.h>
35 #if HAVE_UNISTD_H
36 #include <unistd.h>             /* Prototypes for lseek */
37 #endif
38 #include "mmprivate.h"
39 #include "xbt/ex.h"
40 #include "src/xbt_modinter.h" /* declarations of mmalloc_preinit and friends that live here */
41
42 #ifndef SEEK_SET
43 #define SEEK_SET 0
44 #endif
45
46 /* Initialize access to a mmalloc managed region.
47
48    If FD is a valid file descriptor for an open file then data for the
49    mmalloc managed region is mapped to that file, otherwise an anonymous
50    map is used if supported by the underlying OS. In case of running in
51    an OS without support of anonymous mappings then "/dev/zero" is used
52    and in both cases the data will not exist in any filesystem object.
53
54    If the open file corresponding to FD is from a previous use of
55    mmalloc and passes some basic sanity checks to ensure that it is
56    compatible with the current mmalloc package, then its data is
57    mapped in and is immediately accessible at the same addresses in
58    the current process as the process that created the file (ignoring
59    the BASEADDR parameter).
60
61    For non valid FDs or empty files ones the mapping is established
62    starting at the specified address BASEADDR in the process address
63    space.
64
65    The provided BASEADDR should be choosed carefully in order to avoid
66    bumping into existing mapped regions or future mapped regions.
67
68    On success, returns a "malloc descriptor" which is used in subsequent
69    calls to other mmalloc package functions.  It is explicitly "void *"
70    so that users of the package don't have to worry about the actual
71    implementation details.
72
73    On failure returns NULL. */
74
75 xbt_mheap_t xbt_mheap_new(int fd, void *baseaddr)
76 {
77   return xbt_mheap_new_options(fd, baseaddr, 0);
78 }
79
80 xbt_mheap_t xbt_mheap_new_options(int fd, void *baseaddr, int options)
81 {
82   struct mdesc mtemp;
83   xbt_mheap_t mdp;
84   void *mbase;
85   struct stat sbuf;
86
87   /* First check to see if FD is a valid file descriptor, and if so, see
88      if the file has any current contents (size > 0).  If it does, then
89      attempt to reuse the file.  If we can't reuse the file, either
90      because it isn't a valid mmalloc produced file, was produced by an
91      obsolete version, or any other reason, then we fail to attach to
92      this file. */
93
94   if (fd >= 0) {
95     if (fstat(fd, &sbuf) < 0)
96       return (NULL);
97
98     else if (sbuf.st_size > 0) {
99       /* We were given an valid file descriptor on an open file, so try to remap
100          it into the current process at the same address to which it was previously
101          mapped. It naturally have to pass some sanity checks for that.
102
103          Note that we have to update the file descriptor number in the malloc-
104          descriptor read from the file to match the current valid one, before
105          trying to map the file in, and again after a successful mapping and
106          after we've switched over to using the mapped in malloc descriptor
107          rather than the temporary one on the stack.
108
109          Once we've switched over to using the mapped in malloc descriptor, we
110          have to update the pointer to the morecore function, since it almost
111          certainly will be at a different address if the process reusing the
112          mapped region is from a different executable.
113
114          Also note that if the heap being remapped previously used the mmcheckf()
115          routines, we need to update the hooks since their target functions
116          will have certainly moved if the executable has changed in any way.
117          We do this by calling mmcheckf() internally.
118
119          Returns a pointer to the malloc descriptor if successful, or NULL if
120          unsuccessful for some reason. */
121
122       struct mdesc newmd;
123       struct mdesc* mdptr   = NULL;
124       struct mdesc* mdptemp = NULL;
125
126       if (lseek(fd, 0L, SEEK_SET) != 0)
127         return NULL;
128       if (read(fd, (char *) &newmd, sizeof(newmd)) != sizeof(newmd))
129         return NULL;
130       if (newmd.headersize != sizeof(newmd))
131         return NULL;
132       if (strcmp(newmd.magic, MMALLOC_MAGIC) != 0)
133         return NULL;
134       if (newmd.version > MMALLOC_VERSION)
135         return NULL;
136
137       newmd.fd = fd;
138       if (__mmalloc_remap_core(&newmd) == newmd.base) {
139         mdptr = (struct mdesc *) newmd.base;
140         mdptr->fd = fd;
141         if(!mdptr->refcount){
142           pthread_mutex_init(&mdptr->mutex, NULL);
143           mdptr->refcount++;
144         }
145       }
146
147       /* Add the new heap to the linked list of heaps attached by mmalloc */
148       mdptemp = __mmalloc_default_mdp;
149       while(mdptemp->next_mdesc)
150         mdptemp = mdptemp->next_mdesc;
151
152       LOCK(mdptemp);
153       mdptemp->next_mdesc = mdptr;
154       UNLOCK(mdptemp);
155
156       return mdptr;
157     }
158   }
159
160   /* NULL is not a valid baseaddr as we cannot map anything there. C'mon, user. Think! */
161   if (baseaddr == NULL)
162     return (NULL);
163
164   /* We start off with the malloc descriptor allocated on the stack, until we build it up enough to
165    * call _mmalloc_mmap_morecore() to allocate the first page of the region and copy it there.  Ensure that it is
166    * zero'd and then initialize the fields that we know values for. */
167
168   mdp = &mtemp;
169   memset((char *) mdp, 0, sizeof(mtemp));
170   strncpy(mdp->magic, MMALLOC_MAGIC, MMALLOC_MAGIC_SIZE);
171   mdp->headersize = sizeof(mtemp);
172   mdp->version = MMALLOC_VERSION;
173   mdp->fd = fd;
174   mdp->base = mdp->breakval = mdp->top = baseaddr;
175   mdp->next_mdesc = NULL;
176   mdp->refcount = 1;
177   mdp->options = options;
178
179   /* If we have not been passed a valid open file descriptor for the file
180      to map to, then we go for an anonymous map */
181
182   if (mdp->fd < 0){
183     mdp->flags |= MMALLOC_ANONYMOUS;
184   }
185   pthread_mutex_init(&mdp->mutex, NULL);
186   /* If we have not been passed a valid open file descriptor for the file
187      to map to, then open /dev/zero and use that to map to. */
188
189   /* Now try to map in the first page, copy the malloc descriptor structure there, and arrange to return a pointer to
190    * this new copy.  If the mapping fails, then close the file descriptor if it was opened by us, and arrange to return
191    * a NULL. */
192
193   if ((mbase = mmorecore(mdp, sizeof(mtemp))) != NULL) {
194     memcpy(mbase, mdp, sizeof(mtemp));
195   } else {
196     fprintf(stderr, "morecore failed to get some more memory!\n");
197     abort();
198   }
199
200   /* Add the new heap to the linked list of heaps attached by mmalloc */
201   if(__mmalloc_default_mdp){
202     mdp = __mmalloc_default_mdp;
203     while(mdp->next_mdesc)
204       mdp = mdp->next_mdesc;
205
206     LOCK(mdp);
207     mdp->next_mdesc = (struct mdesc *)mbase;
208     UNLOCK(mdp);
209   }
210
211   return mbase;
212 }
213
214
215
216 /** Terminate access to a mmalloc managed region, but do not free its content.
217  *
218  * This is for example useful for the base region where ldl stores its data
219  *   because it leaves the place after us.
220  */
221 void xbt_mheap_destroy_no_free(xbt_mheap_t md)
222 {
223   struct mdesc *mdp = md;
224
225   if(--mdp->refcount == 0){
226     pthread_mutex_destroy(&mdp->mutex);
227   }
228 }
229
230 /** Terminate access to a mmalloc managed region by unmapping all memory pages associated with the region, and closing
231  *  the file descriptor if it is one that we opened.
232
233     Returns NULL on success.
234
235     Returns the malloc descriptor on failure, which can subsequently be used for further action, such as obtaining more
236     information about the nature of the failure.
237
238     Note that the malloc descriptor that we are using is currently located in region we are about to unmap, so we first
239     make a local copy of it on the stack and use the copy. */
240
241 void *xbt_mheap_destroy(xbt_mheap_t mdp)
242 {
243   if (mdp != NULL) {
244     /* Remove the heap from the linked list of heaps attached by mmalloc */
245     struct mdesc* mdptemp = __mmalloc_default_mdp;
246     while(mdptemp->next_mdesc != mdp )
247       mdptemp = mdptemp->next_mdesc;
248
249     mdptemp->next_mdesc = mdp->next_mdesc;
250
251     xbt_mheap_destroy_no_free(mdp);
252     struct mdesc mtemp = *mdp;
253
254     /* Now unmap all the pages associated with this region by asking for a
255        negative increment equal to the current size of the region. */
256
257     if (mmorecore(&mtemp, (char *)mtemp.base - (char *)mtemp.breakval) == NULL) {
258       /* Deallocating failed.  Update the original malloc descriptor with any changes */
259       *mdp = mtemp;
260     } else {
261       if (mtemp.flags & MMALLOC_DEVZERO) {
262         close(mtemp.fd);
263       }
264       mdp = NULL;
265     }
266   }
267
268   return (mdp);
269 }
270
271 /* Safety gap from the heap's break address.
272  * Try to increase this first if you experience strange errors under valgrind. */
273 #define HEAP_OFFSET   (128UL<<20)
274
275 xbt_mheap_t mmalloc_get_default_md(void)
276 {
277   xbt_assert(__mmalloc_default_mdp);
278   return __mmalloc_default_mdp;
279 }
280
281 static void mmalloc_fork_prepare(void)
282 {
283   xbt_mheap_t mdp = NULL;
284   if ((mdp =__mmalloc_default_mdp)){
285     while(mdp){
286       LOCK(mdp);
287       if(mdp->fd >= 0){
288         mdp->refcount++;
289       }
290       mdp = mdp->next_mdesc;
291     }
292   }
293 }
294
295 static void mmalloc_fork_parent(void)
296 {
297   xbt_mheap_t mdp = NULL;
298   if ((mdp =__mmalloc_default_mdp)){
299     while(mdp){
300       if(mdp->fd < 0)
301         UNLOCK(mdp);
302       mdp = mdp->next_mdesc;
303     }
304   }
305 }
306
307 static void mmalloc_fork_child(void)
308 {
309   struct mdesc* mdp = NULL;
310   if ((mdp =__mmalloc_default_mdp)){
311     while(mdp){
312       UNLOCK(mdp);
313       mdp = mdp->next_mdesc;
314     }
315   }
316 }
317
318 /* Initialize the default malloc descriptor. */
319 void *mmalloc_preinit(void)
320 {
321   if (__mmalloc_default_mdp == NULL) {
322     if(!xbt_pagesize)
323       xbt_pagesize = getpagesize();
324     unsigned long mask = ~((unsigned long)xbt_pagesize - 1);
325     void *addr = (void*)(((unsigned long)sbrk(0) + HEAP_OFFSET) & mask);
326     __mmalloc_default_mdp = xbt_mheap_new_options(-1, addr, XBT_MHEAP_OPTION_MEMSET);
327
328     // atfork mandated at least on FreeBSD, or simgrid-mc will fail to fork the verified app
329     int res = pthread_atfork(mmalloc_fork_prepare, mmalloc_fork_parent, mmalloc_fork_child);
330     xbt_assert(res == 0, "pthread_atfork() failed: return value %d", res);
331   }
332   xbt_assert(__mmalloc_default_mdp != NULL);
333
334   return __mmalloc_default_mdp;
335 }
336
337 void mmalloc_postexit(void)
338 {
339   /* Do not destroy the default mdp or ldl won't be able to free the memory it
340    * allocated since we're in memory */
341   // xbt_mheap_destroy_no_free(__mmalloc_default_mdp);
342 }
343
344 // This is the underlying implementation of mmalloc_get_bytes_used_remote.
345 // Is it used directly in order to evaluate the bytes used from a different
346 // process.
347 size_t mmalloc_get_bytes_used_remote(size_t heaplimit, const malloc_info* heapinfo)
348 {
349   int bytes = 0;
350   for (size_t i=0; i < heaplimit; ++i){
351     if (heapinfo[i].type == MMALLOC_TYPE_UNFRAGMENTED){
352       if (heapinfo[i].busy_block.busy_size > 0)
353         bytes += heapinfo[i].busy_block.busy_size;
354     } else if (heapinfo[i].type > 0) {
355       for (size_t j=0; j < (size_t) (BLOCKSIZE >> heapinfo[i].type); j++){
356         if(heapinfo[i].busy_frag.frag_size[j] > 0)
357           bytes += heapinfo[i].busy_frag.frag_size[j];
358       }
359     }
360   }
361   return bytes;
362 }
363
364 size_t mmalloc_get_bytes_used(const xbt_mheap_t heap){
365   const struct mdesc* heap_data = (const struct mdesc *) heap;
366   return mmalloc_get_bytes_used_remote(heap_data->heaplimit, heap_data->heapinfo);
367 }
368
369 ssize_t mmalloc_get_busy_size(xbt_mheap_t heap, void *ptr){
370
371   ssize_t block = ((char*)ptr - (char*)(heap->heapbase)) / BLOCKSIZE + 1;
372   if(heap->heapinfo[block].type < 0)
373     return -1;
374   else if(heap->heapinfo[block].type == MMALLOC_TYPE_UNFRAGMENTED)
375     return heap->heapinfo[block].busy_block.busy_size;
376   else{
377     ssize_t frag = ((uintptr_t) (ADDR2UINT (ptr) % (BLOCKSIZE))) >> heap->heapinfo[block].type;
378     return heap->heapinfo[block].busy_frag.frag_size[frag];
379   }
380
381 }