Logo AND Algorithmique Numérique Distribuée

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