Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of git+ssh://scm.gforge.inria.fr/gitroot/simgrid/simgrid
[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 <sys/types.h>
31 #include <fcntl.h>              /* After sys/types.h, at least for dpx/2.  */
32 #include <sys/stat.h>
33 #include <string.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>             /* Prototypes for lseek */
36 #endif
37 #include "mmprivate.h"
38 #include "xbt/ex.h"
39 #include "xbt_modinter.h" /* declarations of mmalloc_preinit and friends that live here */
40
41 #ifndef SEEK_SET
42 #define SEEK_SET 0
43 #endif
44
45 /* Initialize access to a mmalloc managed region.
46
47    If FD is a valid file descriptor for an open file then data for the
48    mmalloc managed region is mapped to that file, otherwise an anonymous
49    map is used if supported by the underlying OS. In case of running in
50    an OS without support of anonymous mappings then "/dev/zero" is used 
51    and in both cases the data will not exist in any filesystem object.
52
53    If the open file corresponding to FD is from a previous use of
54    mmalloc and passes some basic sanity checks to ensure that it is
55    compatible with the current mmalloc package, then its data is
56    mapped in and is immediately accessible at the same addresses in
57    the current process as the process that created the file (ignoring
58    the BASEADDR parameter).
59
60    For non valid FDs or empty files ones the mapping is established 
61    starting at the specified address BASEADDR in the process address 
62    space.
63
64    The provided BASEADDR should be choosed carefully in order to avoid
65    bumping into existing mapped regions or future mapped regions.
66
67    On success, returns a "malloc descriptor" which is used in subsequent
68    calls to other mmalloc package functions.  It is explicitly "void *"
69    so that users of the package don't have to worry about the actual
70    implementation details.
71
72    On failure returns NULL. */
73
74 xbt_mheap_t xbt_mheap_new(int fd, void *baseaddr)
75 {
76   return xbt_mheap_new_options(fd, baseaddr, 0);
77 }
78
79 xbt_mheap_t xbt_mheap_new_options(int fd, void *baseaddr, int options)
80 {
81   struct mdesc mtemp;
82   xbt_mheap_t mdp;
83   void *mbase;
84   struct stat sbuf;
85
86   /* First check to see if FD is a valid file descriptor, and if so, see
87      if the file has any current contents (size > 0).  If it does, then
88      attempt to reuse the file.  If we can't reuse the file, either
89      because it isn't a valid mmalloc produced file, was produced by an
90      obsolete version, or any other reason, then we fail to attach to
91      this file. */
92
93   if (fd >= 0) {
94     if (fstat(fd, &sbuf) < 0)
95       return (NULL);
96
97     else if (sbuf.st_size > 0) {
98       /* We were given an valid file descriptor on an open file, so try to remap
99          it into the current process at the same address to which it was previously
100          mapped. It naturally have to pass some sanity checks for that.
101
102          Note that we have to update the file descriptor number in the malloc-
103          descriptor read from the file to match the current valid one, before
104          trying to map the file in, and again after a successful mapping and
105          after we've switched over to using the mapped in malloc descriptor
106          rather than the temporary one on the stack.
107
108          Once we've switched over to using the mapped in malloc descriptor, we
109          have to update the pointer to the morecore function, since it almost
110          certainly will be at a different address if the process reusing the
111          mapped region is from a different executable.
112
113          Also note that if the heap being remapped previously used the mmcheckf()
114          routines, we need to update the hooks since their target functions
115          will have certainly moved if the executable has changed in any way.
116          We do this by calling mmcheckf() internally.
117
118          Returns a pointer to the malloc descriptor if successful, or NULL if
119          unsuccessful for some reason. */
120
121       struct mdesc newmd;
122       struct mdesc *mdptr = NULL, *mdptemp = NULL;
123
124       if (lseek(fd, 0L, SEEK_SET) != 0)
125         return NULL;
126       if (read(fd, (char *) &newmd, sizeof(newmd)) != sizeof(newmd))
127         return NULL;
128       if (newmd.headersize != sizeof(newmd))
129         return NULL;
130       if (strcmp(newmd.magic, MMALLOC_MAGIC) != 0)
131         return NULL;
132       if (newmd.version > MMALLOC_VERSION)
133         return NULL;
134
135       newmd.fd = fd;
136       if (__mmalloc_remap_core(&newmd) == newmd.base) {
137         mdptr = (struct mdesc *) newmd.base;
138         mdptr->fd = fd;
139         if(!mdptr->refcount){
140           pthread_mutex_init(&mdptr->mutex, NULL);
141           mdptr->refcount++;
142         }
143       }
144
145       /* Add the new heap to the linked list of heaps attached by mmalloc */
146       mdptemp = __mmalloc_default_mdp;
147       while(mdptemp->next_mdesc)
148         mdptemp = mdptemp->next_mdesc;
149
150       LOCK(mdptemp);
151       mdptemp->next_mdesc = mdptr;
152       UNLOCK(mdptemp);
153
154       return mdptr;
155     }
156   }
157
158   /* NULL is not a valid baseaddr as we cannot map anything there.
159      C'mon, user. Think! */
160   if (baseaddr == NULL)
161     return (NULL);
162
163   /* We start off with the malloc descriptor allocated on the stack, until
164      we build it up enough to call _mmalloc_mmap_morecore() to allocate the
165      first page of the region and copy it there.  Ensure that it is zero'd and
166      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
190      there, and arrange to return a pointer to this new copy.  If the mapping
191      fails, then close the file descriptor if it was opened by us, and arrange
192      to return a NULL. */
193
194   if ((mbase = mmorecore(mdp, sizeof(mtemp))) != NULL) {
195     memcpy(mbase, mdp, sizeof(mtemp));
196   } else {
197     fprintf(stderr, "morecore failed to get some more memory!\n");
198     abort();
199   }
200
201   /* Add the new heap to the linked list of heaps attached by mmalloc */  
202   if(__mmalloc_default_mdp){
203     mdp = __mmalloc_default_mdp;
204     while(mdp->next_mdesc)
205       mdp = mdp->next_mdesc;
206
207     LOCK(mdp);
208     mdp->next_mdesc = (struct mdesc *)mbase;
209     UNLOCK(mdp);
210   }
211
212   return mbase;
213 }
214
215
216
217 /** Terminate access to a mmalloc managed region, but do not free its content.
218  *
219  * This is for example useful for the base region where ldl stores its data
220  *   because it leaves the place after us.
221  */
222 void xbt_mheap_destroy_no_free(xbt_mheap_t md)
223 {
224   struct mdesc *mdp = md;
225
226   if(--mdp->refcount == 0){
227     pthread_mutex_destroy(&mdp->mutex);
228   }
229 }
230
231 /** Terminate access to a mmalloc managed region by unmapping all memory pages
232     associated with the region, and closing the file descriptor if it is one
233     that we opened.
234
235     Returns NULL on success.
236
237     Returns the malloc descriptor on failure, which can subsequently be used
238     for further action, such as obtaining more information about the nature of
239     the failure.
240
241     Note that the malloc descriptor that we are using is currently located in
242     region we are about to unmap, so we first make a local copy of it on the
243     stack and use the copy. */
244
245 void *xbt_mheap_destroy(xbt_mheap_t mdp)
246 {
247   struct mdesc mtemp, *mdptemp;
248
249   if (mdp != NULL) {
250     /* Remove the heap from the linked list of heaps attached by mmalloc */
251     mdptemp = __mmalloc_default_mdp;
252     while(mdptemp->next_mdesc != mdp )
253       mdptemp = mdptemp->next_mdesc;
254
255     mdptemp->next_mdesc = mdp->next_mdesc;
256
257     xbt_mheap_destroy_no_free(mdp);
258     mtemp = *mdp;
259
260     /* Now unmap all the pages associated with this region by asking for a
261        negative increment equal to the current size of the region. */
262
263     if (mmorecore(&mtemp, (char *)mtemp.base - (char *)mtemp.breakval) == NULL) {
264       /* Deallocating failed.  Update the original malloc descriptor
265          with any changes */
266       *mdp = mtemp;
267     } else {
268       if (mtemp.flags & MMALLOC_DEVZERO) {
269         close(mtemp.fd);
270       }
271       mdp = NULL;
272     }
273   }
274
275   return (mdp);
276 }
277
278 /* Safety gap from the heap's break address.
279  * Try to increase this first if you experience strange errors under
280  * valgrind. */
281 #define HEAP_OFFSET   (128UL<<20)
282
283 xbt_mheap_t mmalloc_get_default_md(void)
284 {
285   xbt_assert(__mmalloc_default_mdp);
286   return __mmalloc_default_mdp;
287 }
288
289 static void mmalloc_fork_prepare(void)
290 {
291   xbt_mheap_t mdp = NULL;
292   if ((mdp =__mmalloc_default_mdp)){
293     while(mdp){
294       LOCK(mdp);
295       if(mdp->fd >= 0){
296         mdp->refcount++;
297       }
298       mdp = mdp->next_mdesc;
299     }
300   }
301 }
302
303 static void mmalloc_fork_parent(void)
304 {
305   xbt_mheap_t mdp = NULL;
306   if ((mdp =__mmalloc_default_mdp)){
307     while(mdp){
308       if(mdp->fd < 0)
309         UNLOCK(mdp);
310       mdp = mdp->next_mdesc;
311     }
312   }
313 }
314
315 static void mmalloc_fork_child(void)
316 {
317   struct mdesc* mdp = NULL;
318   if ((mdp =__mmalloc_default_mdp)){
319     while(mdp){
320       UNLOCK(mdp);
321       mdp = mdp->next_mdesc;
322     }
323   }
324 }
325
326 /* Initialize the default malloc descriptor. */
327 void *mmalloc_preinit(void)
328 {
329   int res;
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     res = xbt_os_thread_atfork(mmalloc_fork_prepare,  
340                                mmalloc_fork_parent, mmalloc_fork_child);
341     if (res != 0)
342       THROWF(system_error,0,"xbt_os_thread_atfork() failed: return value %d",res);
343   }
344   xbt_assert(__mmalloc_default_mdp != NULL);
345
346   return __mmalloc_default_mdp;
347 }
348
349 void mmalloc_postexit(void)
350 {
351   /* Do not destroy the default mdp or ldl won't be able to free the memory it
352    * allocated since we're in memory */
353   // xbt_mheap_destroy_no_free(__mmalloc_default_mdp);
354 }
355
356 // This is the underlying implementation of mmalloc_get_bytes_used_remote.
357 // Is it used directly in order to evaluate the bytes used from a different
358 // process.
359 size_t mmalloc_get_bytes_used_remote(size_t heaplimit, const malloc_info* heapinfo)
360 {
361   int bytes = 0;
362   for (size_t i=0; i<=heaplimit; ++i){
363     if (heapinfo[i].type == MMALLOC_TYPE_UNFRAGMENTED){
364       if (heapinfo[i].busy_block.busy_size > 0)
365         bytes += heapinfo[i].busy_block.busy_size;
366     } else if (heapinfo[i].type > 0) {
367       for (size_t j=0; j < (size_t) (BLOCKSIZE >> heapinfo[i].type); j++){
368         if(heapinfo[i].busy_frag.frag_size[j] > 0)
369           bytes += heapinfo[i].busy_frag.frag_size[j];
370       }
371     }
372   }
373   return bytes;
374 }
375
376 size_t mmalloc_get_bytes_used(const xbt_mheap_t heap){
377   const struct mdesc* heap_data = (const struct mdesc *) heap;
378   return mmalloc_get_bytes_used_remote(heap_data->heaplimit, heap_data->heapinfo);
379 }
380
381 ssize_t mmalloc_get_busy_size(xbt_mheap_t heap, void *ptr){
382
383   ssize_t block = ((char*)ptr - (char*)(heap->heapbase)) / BLOCKSIZE + 1;
384   if(heap->heapinfo[block].type < 0)
385     return -1;
386   else if(heap->heapinfo[block].type == MMALLOC_TYPE_UNFRAGMENTED)
387     return heap->heapinfo[block].busy_block.busy_size;
388   else{
389     ssize_t frag = ((uintptr_t) (ADDR2UINT (ptr) % (BLOCKSIZE))) >> heap->heapinfo[block].type;
390     return heap->heapinfo[block].busy_frag.frag_size[frag];
391   }
392     
393 }
394
395 void mmcheck(xbt_mheap_t heap) {return;
396   if (!heap->heapinfo)
397     return;
398   malloc_info* heapinfo = NULL;
399   for (size_t i=1; i < heap->heaplimit; i += mmalloc_get_increment(heapinfo)) {
400     heapinfo = heap->heapinfo + i;
401     switch (heapinfo->type) {
402     case MMALLOC_TYPE_HEAPINFO:
403     case MMALLOC_TYPE_FREE:
404       if (heapinfo->free_block.size==0) {
405         xbt_die("Block size == 0");
406       }
407       break;
408     case MMALLOC_TYPE_UNFRAGMENTED:
409       if (heapinfo->busy_block.size==0) {
410         xbt_die("Block size == 0");
411       }
412       if (heapinfo->busy_block.busy_size==0 && heapinfo->busy_block.size!=0) {
413         xbt_die("Empty busy block");
414       }
415       break;
416     default:
417       if (heapinfo->type<0) {
418         xbt_die("Unkown mmalloc block type.");
419       }
420     }
421   }
422 }