Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
some optimizations
[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-2014. 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, *mdptemp = NULL;
124
125       if (lseek(fd, 0L, SEEK_SET) != 0)
126         return NULL;
127       if (read(fd, (char *) &newmd, sizeof(newmd)) != sizeof(newmd))
128         return NULL;
129       if (newmd.headersize != sizeof(newmd))
130         return NULL;
131       if (strcmp(newmd.magic, MMALLOC_MAGIC) != 0)
132         return NULL;
133       if (newmd.version > MMALLOC_VERSION)
134         return NULL;
135
136       newmd.fd = fd;
137       if (__mmalloc_remap_core(&newmd) == newmd.base) {
138         mdptr = (struct mdesc *) newmd.base;
139         mdptr->fd = fd;
140         if(!mdptr->refcount){
141           pthread_mutex_init(&mdptr->mutex, NULL);
142           mdptr->refcount++;
143         }
144       }
145
146       /* Add the new heap to the linked list of heaps attached by mmalloc */
147       mdptemp = __mmalloc_default_mdp;
148       while(mdptemp->next_mdesc)
149         mdptemp = mdptemp->next_mdesc;
150
151       LOCK(mdptemp);
152       mdptemp->next_mdesc = mdptr;
153       UNLOCK(mdptemp);
154
155       return mdptr;
156     }
157   }
158
159   /* NULL is not a valid baseaddr as we cannot map anything there.
160      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
165      we build it up enough to call _mmalloc_mmap_morecore() to allocate the
166      first page of the region and copy it there.  Ensure that it is zero'd and
167      then initialize the fields that we know values for. */
168
169   mdp = &mtemp;
170   memset((char *) mdp, 0, sizeof(mtemp));
171   strncpy(mdp->magic, MMALLOC_MAGIC, MMALLOC_MAGIC_SIZE);
172   mdp->headersize = sizeof(mtemp);
173   mdp->version = MMALLOC_VERSION;
174   mdp->fd = fd;
175   mdp->base = mdp->breakval = mdp->top = baseaddr;
176   mdp->next_mdesc = NULL;
177   mdp->refcount = 1;
178   mdp->options = options;
179
180   /* If we have not been passed a valid open file descriptor for the file
181      to map to, then we go for an anonymous map */
182
183   if (mdp->fd < 0){
184     mdp->flags |= MMALLOC_ANONYMOUS;
185   }
186   pthread_mutex_init(&mdp->mutex, NULL);
187   /* If we have not been passed a valid open file descriptor for the file
188      to map to, then open /dev/zero and use that to map to. */
189
190   /* Now try to map in the first page, copy the malloc descriptor structure
191      there, and arrange to return a pointer to this new copy.  If the mapping
192      fails, then close the file descriptor if it was opened by us, and arrange
193      to return a NULL. */
194
195   if ((mbase = mmorecore(mdp, sizeof(mtemp))) != NULL) {
196     memcpy(mbase, mdp, sizeof(mtemp));
197   } else {
198     fprintf(stderr, "morecore failed to get some more memory!\n");
199     abort();
200   }
201
202   /* Add the new heap to the linked list of heaps attached by mmalloc */
203   if(__mmalloc_default_mdp){
204     mdp = __mmalloc_default_mdp;
205     while(mdp->next_mdesc)
206       mdp = mdp->next_mdesc;
207
208     LOCK(mdp);
209     mdp->next_mdesc = (struct mdesc *)mbase;
210     UNLOCK(mdp);
211   }
212
213   return mbase;
214 }
215
216
217
218 /** Terminate access to a mmalloc managed region, but do not free its content.
219  *
220  * This is for example useful for the base region where ldl stores its data
221  *   because it leaves the place after us.
222  */
223 void xbt_mheap_destroy_no_free(xbt_mheap_t md)
224 {
225   struct mdesc *mdp = md;
226
227   if(--mdp->refcount == 0){
228     pthread_mutex_destroy(&mdp->mutex);
229   }
230 }
231
232 /** Terminate access to a mmalloc managed region by unmapping all memory pages
233     associated with the region, and closing the file descriptor if it is one
234     that we opened.
235
236     Returns NULL on success.
237
238     Returns the malloc descriptor on failure, which can subsequently be used
239     for further action, such as obtaining more information about the nature of
240     the failure.
241
242     Note that the malloc descriptor that we are using is currently located in
243     region we are about to unmap, so we first make a local copy of it on the
244     stack and use the copy. */
245
246 void *xbt_mheap_destroy(xbt_mheap_t mdp)
247 {
248   struct mdesc mtemp, *mdptemp;
249
250   if (mdp != NULL) {
251     /* Remove the heap from the linked list of heaps attached by mmalloc */
252     mdptemp = __mmalloc_default_mdp;
253     while(mdptemp->next_mdesc != mdp )
254       mdptemp = mdptemp->next_mdesc;
255
256     mdptemp->next_mdesc = mdp->next_mdesc;
257
258     xbt_mheap_destroy_no_free(mdp);
259     mtemp = *mdp;
260
261     /* Now unmap all the pages associated with this region by asking for a
262        negative increment equal to the current size of the region. */
263
264     if (mmorecore(&mtemp, (char *)mtemp.base - (char *)mtemp.breakval) == NULL) {
265       /* Deallocating failed.  Update the original malloc descriptor
266          with any changes */
267       *mdp = mtemp;
268     } else {
269       if (mtemp.flags & MMALLOC_DEVZERO) {
270         close(mtemp.fd);
271       }
272       mdp = NULL;
273     }
274   }
275
276   return (mdp);
277 }
278
279 /* Safety gap from the heap's break address.
280  * Try to increase this first if you experience strange errors under
281  * valgrind. */
282 #define HEAP_OFFSET   (128UL<<20)
283
284 xbt_mheap_t mmalloc_get_default_md(void)
285 {
286   xbt_assert(__mmalloc_default_mdp);
287   return __mmalloc_default_mdp;
288 }
289
290 static void mmalloc_fork_prepare(void)
291 {
292   xbt_mheap_t mdp = NULL;
293   if ((mdp =__mmalloc_default_mdp)){
294     while(mdp){
295       LOCK(mdp);
296       if(mdp->fd >= 0){
297         mdp->refcount++;
298       }
299       mdp = mdp->next_mdesc;
300     }
301   }
302 }
303
304 static void mmalloc_fork_parent(void)
305 {
306   xbt_mheap_t mdp = NULL;
307   if ((mdp =__mmalloc_default_mdp)){
308     while(mdp){
309       if(mdp->fd < 0)
310         UNLOCK(mdp);
311       mdp = mdp->next_mdesc;
312     }
313   }
314 }
315
316 static void mmalloc_fork_child(void)
317 {
318   struct mdesc* mdp = NULL;
319   if ((mdp =__mmalloc_default_mdp)){
320     while(mdp){
321       UNLOCK(mdp);
322       mdp = mdp->next_mdesc;
323     }
324   }
325 }
326
327 /* Initialize the default malloc descriptor. */
328 void *mmalloc_preinit(void)
329 {
330   if (__mmalloc_default_mdp == NULL) {
331     if(!xbt_pagesize)
332       xbt_pagesize = getpagesize();
333     unsigned long mask = ~((unsigned long)xbt_pagesize - 1);
334     void *addr = (void*)(((unsigned long)sbrk(0) + HEAP_OFFSET) & mask);
335     __mmalloc_default_mdp = xbt_mheap_new_options(-1, addr, XBT_MHEAP_OPTION_MEMSET);
336     /* Fixme? only the default mdp in protected against forks */
337     // This is mandated to protect the mmalloced areas through forks. Think of tesh.
338     // Nah, removing the mutex isn't a good idea either for tesh
339     int res = xbt_os_thread_atfork(mmalloc_fork_prepare, mmalloc_fork_parent, mmalloc_fork_child);
340     if (res != 0)
341       THROWF(system_error,0,"xbt_os_thread_atfork() failed: return value %d",res);
342   }
343   xbt_assert(__mmalloc_default_mdp != NULL);
344
345   return __mmalloc_default_mdp;
346 }
347
348 void mmalloc_postexit(void)
349 {
350   /* Do not destroy the default mdp or ldl won't be able to free the memory it
351    * allocated since we're in memory */
352   // xbt_mheap_destroy_no_free(__mmalloc_default_mdp);
353 }
354
355 // This is the underlying implementation of mmalloc_get_bytes_used_remote.
356 // Is it used directly in order to evaluate the bytes used from a different
357 // process.
358 size_t mmalloc_get_bytes_used_remote(size_t heaplimit, const malloc_info* heapinfo)
359 {
360   int bytes = 0;
361   for (size_t i=0; i < heaplimit; ++i){
362     if (heapinfo[i].type == MMALLOC_TYPE_UNFRAGMENTED){
363       if (heapinfo[i].busy_block.busy_size > 0)
364         bytes += heapinfo[i].busy_block.busy_size;
365     } else if (heapinfo[i].type > 0) {
366       for (size_t j=0; j < (size_t) (BLOCKSIZE >> heapinfo[i].type); j++){
367         if(heapinfo[i].busy_frag.frag_size[j] > 0)
368           bytes += heapinfo[i].busy_frag.frag_size[j];
369       }
370     }
371   }
372   return bytes;
373 }
374
375 size_t mmalloc_get_bytes_used(const xbt_mheap_t heap){
376   const struct mdesc* heap_data = (const struct mdesc *) heap;
377   return mmalloc_get_bytes_used_remote(heap_data->heaplimit, heap_data->heapinfo);
378 }
379
380 ssize_t mmalloc_get_busy_size(xbt_mheap_t heap, void *ptr){
381
382   ssize_t block = ((char*)ptr - (char*)(heap->heapbase)) / BLOCKSIZE + 1;
383   if(heap->heapinfo[block].type < 0)
384     return -1;
385   else if(heap->heapinfo[block].type == MMALLOC_TYPE_UNFRAGMENTED)
386     return heap->heapinfo[block].busy_block.busy_size;
387   else{
388     ssize_t frag = ((uintptr_t) (ADDR2UINT (ptr) % (BLOCKSIZE))) >> heap->heapinfo[block].type;
389     return heap->heapinfo[block].busy_frag.frag_size[frag];
390   }
391
392 }
393
394 void mmcheck(xbt_mheap_t heap) {return;
395   if (!heap->heapinfo)
396     return;
397   malloc_info* heapinfo = NULL;
398   for (size_t i=1; i < heap->heaplimit; i += mmalloc_get_increment(heapinfo)) {
399     heapinfo = heap->heapinfo + i;
400     switch (heapinfo->type) {
401     case MMALLOC_TYPE_HEAPINFO:
402     case MMALLOC_TYPE_FREE:
403       if (heapinfo->free_block.size==0) {
404         xbt_die("Block size == 0");
405       }
406       break;
407     case MMALLOC_TYPE_UNFRAGMENTED:
408       if (heapinfo->busy_block.size==0) {
409         xbt_die("Block size == 0");
410       }
411       if (heapinfo->busy_block.busy_size==0 && heapinfo->busy_block.size!=0) {
412         xbt_die("Empty busy block");
413       }
414       break;
415     default:
416       if (heapinfo->type<0) {
417         xbt_die("Unkown mmalloc block type.");
418       }
419     }
420   }
421 }