55b7b8e307be022db263b6ded57cd57f6bb418f8
[alexxy/gromacs.git] / src / gmxlib / smalloc.c
1 /*
2  * 
3  *                This source code is part of
4  * 
5  *                 G   R   O   M   A   C   S
6  * 
7  *          GROningen MAchine for Chemical Simulations
8  * 
9  *                        VERSION 3.2.0
10  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12  * Copyright (c) 2001-2004, The GROMACS development team,
13  * check out http://www.gromacs.org for more information.
14
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  * 
20  * If you want to redistribute modifications, please consider that
21  * scientific software is very special. Version control is crucial -
22  * bugs must be traceable. We will be happy to consider code for
23  * inclusion in the official distribution, but derived work must not
24  * be called official GROMACS. Details are found in the README & COPYING
25  * files - if they are missing, get the official version at www.gromacs.org.
26  * 
27  * To help us fund GROMACS development, we humbly ask that you cite
28  * the papers on the package - you can find them in the top README file.
29  * 
30  * For more info, check our website at http://www.gromacs.org
31  * 
32  * And Hey:
33  * GROningen Mixture of Alchemy and Childrens' Stories
34  */
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 /* This file is completely threadsafe - keep it that way! */
40
41 #ifdef GMX_THREADS
42 #include "thread_mpi/threads.h"
43 #endif 
44
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include "gmx_fatal.h"
50 #include "smalloc.h"
51 #include "main.h"
52 #ifdef WITH_DMALLOC
53 #include "dmalloc.h"
54 #endif
55
56 #ifdef DEBUG
57 static void log_action(int bMal,const char *what,const char *file,int line,
58                        int nelem,int size,void *ptr)
59 {
60   static int btot=0;
61   char *NN = "NULL";
62   int        bytes;
63   
64   bytes=size*nelem;
65   if (!bMal)
66     bytes=-bytes;
67   
68 #ifdef GMX_THREADS
69   tMPI_Thread_mutex_lock(&gmx_logfile_mtx);
70 #endif
71
72   /* This total memory count is not correct, since with realloc
73    * it adds the whole size again, not just the increment.
74    */
75   /* This static variable is protected by the mutex too... */
76   btot+=bytes;
77     
78   bytes/=1024;
79   if (debug && (bytes != 0)) {
80     fprintf(debug,"%s:%d kB (%7d kB) [%s, line %d, nelem %d, size %d]\n",
81             what ? what : NN,bytes,btot/1024,
82             file ? file : NN,line,nelem,size);
83   }
84   /* Print to stderr for things larger than 1 MB */
85   if (bytes >= 1024 || bytes <= -1024) {
86     char *fname=NULL;
87     if (file) {
88       fname = strrchr(file,DIR_SEPARATOR);
89       if (fname) {
90         fname++;
91       } else {
92         fname = file;
93       }
94     }
95     printf("%s: %.1f MB [%s, line %d, nelem %d, size %d]\n",
96            what ? what  : NN,bytes/1024.0,
97            file ? fname : NN,line,nelem,size);
98   }
99 #ifdef GMX_THREADS
100   tMPI_Thread_mutex_unlock(&gmx_logfile_mtx);
101 #endif
102 }
103 #endif
104
105 static char *gmx_large_int_str(gmx_large_int_t i,char *buf)
106 {
107   sprintf(buf,gmx_large_int_pfmt,i);
108
109   return buf;
110 }
111
112 void *save_malloc(const char *name,const char *file,int line,size_t size)
113 {
114   void *p;
115   
116   p=NULL;
117   if (size==0)
118     p=NULL;
119   else
120     {
121       if ((p=malloc(size))==NULL) {
122         char cbuf[22];
123         gmx_fatal(errno,__FILE__,__LINE__,
124                   "Not enough memory. Failed to malloc %s bytes for %s\n"
125                   "(called from file %s, line %d)",
126                   gmx_large_int_str((gmx_large_int_t)size,cbuf),
127                   name,file,line);
128       }
129       (void) memset(p,0,size);
130     }
131 #ifdef DEBUG
132   log_action(1,name,file,line,1,size,p);
133 #endif
134   return p;
135 }
136
137 void *save_calloc(const char *name,const char *file,int line,
138                   size_t nelem,size_t elsize)
139 {
140   void *p;
141   
142   p=NULL;
143   if ((nelem==0)||(elsize==0))
144     p=NULL;
145   else
146     {
147 #ifdef PRINT_ALLOC_KB
148       int rank=0;
149       if (nelem*elsize >= PRINT_ALLOC_KB*1024) {
150 #ifdef GMX_MPI
151 #include <mpi.h>
152         MPI_Comm_rank(MPI_COMM_WORLD,&rank);
153 #endif
154         printf("Allocating %.1f MB for %s (called from file %s, line %d on %d)\n",
155                nelem*elsize/1048576.0,name,file,line,rank);
156       }
157 #endif
158 #ifdef GMX_BROKEN_CALLOC
159       /* emulate calloc(3) with malloc/memset on machines with 
160          a broken calloc, e.g. in -lgmalloc on cray xt3. */
161       if ((p=malloc((size_t)nelem*(size_t)elsize))==NULL) 
162         gmx_fatal(errno,__FILE__,__LINE__,
163                   "Not enough memory. Failed to calloc %"gmx_large_int_fmt
164                   " elements of size %"gmx_large_int_fmt
165                   " for %s\n(called from file %s, line %d)",
166                   (gmx_large_int_t)nelem,(gmx_large_int_t)elsize,
167                   name,file,line);
168       memset(p, 0,(size_t) (nelem * elsize));
169 #else
170       if ((p=calloc((size_t)nelem,(size_t)elsize))==NULL) 
171         gmx_fatal(errno,__FILE__,__LINE__,
172                   "Not enough memory. Failed to calloc %"gmx_large_int_fmt
173                   " elements of size %"gmx_large_int_fmt
174                   " for %s\n(called from file %s, line %d)",
175                   (gmx_large_int_t)nelem,(gmx_large_int_t)elsize,name,file,line);
176 #endif
177     }
178 #ifdef DEBUG
179   log_action(1,name,file,line,nelem,elsize,p);
180 #endif
181   return p;
182 }
183
184 void *save_realloc(const char *name,const char *file,int line,void *ptr,
185                    size_t nelem,size_t elsize)
186 {
187   void *p;
188   size_t size = nelem*elsize;
189   
190   p=NULL;
191   if (size==0)
192     {
193       save_free(name, file, line, ptr);
194     }
195   else
196     {
197 #ifdef PRINT_ALLOC_KB
198       int rank=0;
199       if (size >= PRINT_ALLOC_KB*1024) {
200 #ifdef GMX_MPI
201 #include <mpi.h>
202         MPI_Comm_rank(MPI_COMM_WORLD,&rank);
203 #endif
204         printf("Reallocating %.1f MB for %s (called from file %s, line %d on %d)\n",
205                size/1048576.0,name,file,line,rank);
206       }
207 #endif
208       if (ptr==NULL) 
209         p=malloc((size_t)size); 
210       else 
211         p=realloc(ptr,(size_t)size);
212       if (p == NULL) {
213         char cbuf[22];
214         gmx_fatal(errno,__FILE__,__LINE__,
215                   "Not enough memory. Failed to realloc %s bytes for %s, %s=%x\n"
216                   "(called from file %s, line %d)",
217                   gmx_large_int_str((gmx_large_int_t)size,cbuf),
218                   name,name,ptr,file,line);
219       }
220 #ifdef DEBUG
221       log_action(1,name,file,line,1,size,p);
222 #endif
223     }
224   return p;
225 }
226
227 void save_free(const char *name,const char *file,int line, void *ptr)
228 {
229 #ifdef DEBUG
230   log_action(0,name,file,line,0,0,ptr);
231 #endif
232   if (ptr != NULL)
233     free(ptr);
234 }
235
236 size_t maxavail(void)
237 {
238   char *ptr;
239   size_t low,high,size;
240   
241   low=0;
242   high=256e6;
243   while ((high-low) > 4) {
244     size=(high+low)/2;
245     if ((ptr=(char *)malloc((size_t)size))==NULL)
246       high=size;
247     else {
248       free(ptr);
249       low=size;
250     }
251   }
252   return low;
253 }
254
255 size_t memavail(void)
256 {
257   char *ptr;
258   size_t size;
259   
260   size = maxavail(); 
261   if (size != 0) { 
262     if ((ptr=(char *)malloc((size_t)size)) != NULL) {
263       size += memavail();
264       free(ptr);
265     }
266   }
267   return size;
268 }
269
270 /* If we don't have useful routines for allocating aligned memory,
271  * then we have to use the old-style GROMACS approach bitwise-ANDing
272  * pointers to ensure alignment. We store the pointer to the originally
273  * allocated region in the space before the returned pointer */
274
275 /* we create a positive define for the absence of an system-provided memalign */
276 #if (!defined HAVE_POSIX_MEMALIGN && !defined HAVE_MEMALIGN && \
277      !defined HAVE__ALIGNED_MALLOC)
278 #define GMX_OWN_MEMALIGN
279 #endif
280
281
282 /* Pointers allocated with this routine should only be freed
283  * with save_free_aligned, however this will only matter
284  * on systems that lack posix_memalign() and memalign() when 
285  * freeing memory that needed to be adjusted to achieve
286  * the necessary alignment. */
287 void *save_calloc_aligned(const char *name,const char *file,int line,
288                           unsigned nelem,size_t elsize,size_t alignment)
289 {
290     void **aligned=NULL;
291     void *malloced=NULL;
292     gmx_bool allocate_fail;
293
294     if (alignment == 0)
295     {
296         gmx_fatal(errno,__FILE__,__LINE__,
297                   "Cannot allocate aligned memory with alignment of zero!\n(called from file %s, line %d)",file,line);
298     }
299
300     
301     if (nelem ==0 || elsize == 0)
302     {
303         aligned  = NULL;
304     }
305     else
306     {
307 #ifdef PRINT_ALLOC_KB
308         if (nelem*elsize >= PRINT_ALLOC_KB*1024)
309         {
310             printf("Allocating %.1f MB for %s\n",
311                    nelem*elsize/(PRINT_ALLOC_KB*1024.0),name);
312         }
313 #endif
314
315         allocate_fail = FALSE; /* stop compiler warnings */
316 #ifdef HAVE_POSIX_MEMALIGN
317         allocate_fail = (0!=posix_memalign(&malloced, alignment, nelem*elsize));
318 #elif defined HAVE_MEMALIGN
319         allocate_fail = ((malloced=memalign(alignment, nelem*elsize)) == NULL);
320 #elif defined HAVE__ALIGNED_MALLOC
321         allocate_fail = ((malloced=_aligned_malloc(nelem*elsize, alignment)) 
322                          == NULL);
323 #else
324         allocate_fail = ((malloced = malloc(nelem*elsize+alignment+
325                                             sizeof(void*)))==NULL);
326 #endif
327         if (allocate_fail)
328         {
329             gmx_fatal(errno,__FILE__,__LINE__,
330                       "Not enough memory. Failed to allocate %u aligned elements of size %u for %s\n(called from file %s, line %d)",nelem,elsize,name,file,line);
331         }
332         /* we start with the original pointer */
333         aligned=(void**)malloced;
334   
335 #ifdef GMX_OWN_MEMALIGN
336         /* Make the aligned pointer, and save the underlying pointer that
337          * we're allowed to free(). */
338
339         /* we first make space to store that underlying pointer: */
340         aligned = aligned + 1; 
341         /* then we apply a bit mask */
342         aligned = (void *) (((size_t) aligned + alignment - 1) & 
343                             (~((size_t) (alignment-1))));
344         /* and we store the original pointer in the area just before the 
345            pointer we're going to return */
346         aligned[-1] = malloced;
347 #endif
348         memset(aligned, 0,(size_t) (nelem * elsize));
349     }
350     return (void*)aligned;
351 }
352
353 /* This routine can NOT be called with any pointer */
354 void save_free_aligned(const char *name,const char *file,int line,void *ptr)
355 {
356     int i, j;
357     void *free=ptr;
358
359     if (NULL != ptr)
360     {
361 #ifdef GMX_OWN_MEMALIGN 
362         /* we get the pointer from just before the memaligned pointer */
363         free= ((void**)ptr)[-1];
364 #endif
365
366 #ifndef HAVE__ALIGNED_MALLOC
367         /* (Now) we're allowed to use a normal free() on this pointer. */
368         save_free(name,file,line,free);
369 #else
370         _aligned_free(free);
371 #endif
372     }
373 }
374