xref: /petsc/src/sys/memory/mal.c (revision 70baa948e42e6bb1660071283af10f413c9269b6)
1 
2 /*
3     Code that allows a user to dictate what malloc() PETSc uses.
4 */
5 #include <petscsys.h>             /*I   "petscsys.h"   I*/
6 #if defined(PETSC_HAVE_MALLOC_H)
7 #include <malloc.h>
8 #endif
9 
10 /*
11         We want to make sure that all mallocs of double or complex numbers are complex aligned.
12     1) on systems with memalign() we call that routine to get an aligned memory location
13     2) on systems without memalign() we
14        - allocate one sizeof(PetscScalar) extra space
15        - we shift the pointer up slightly if needed to get PetscScalar aligned
16        - if shifted we store at ptr[-1] the amount of shift (plus a classid)
17 */
18 #define SHIFT_CLASSID 456123
19 
20 #undef __FUNCT__
21 #define __FUNCT__ "PetscMallocAlign"
22 PetscErrorCode  PetscMallocAlign(size_t mem,int line,const char func[],const char file[],void **result)
23 {
24   if (!mem) { *result = NULL; return 0; }
25 #if defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)
26   *result = malloc(mem);
27 #elif defined(PETSC_HAVE_MEMALIGN)
28   *result = memalign(PETSC_MEMALIGN,mem);
29 #else
30   {
31     /*
32       malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned
33     */
34     int *ptr = (int*)malloc(mem + 2*PETSC_MEMALIGN);
35     if (ptr) {
36       int shift    = (int)(((PETSC_UINTPTR_T) ptr) % PETSC_MEMALIGN);
37       shift        = (2*PETSC_MEMALIGN - shift)/sizeof(int);
38       ptr[shift-1] = shift + SHIFT_CLASSID;
39       ptr         += shift;
40       *result      = (void*)ptr;
41     } else {
42       *result      = NULL;
43     }
44   }
45 #endif
46   if (!*result) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem);
47   return 0;
48 }
49 
50 #undef __FUNCT__
51 #define __FUNCT__ "PetscFreeAlign"
52 PetscErrorCode  PetscFreeAlign(void *ptr,int line,const char func[],const char file[])
53 {
54   if (!ptr) return 0;
55 #if (!(defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) && !defined(PETSC_HAVE_MEMALIGN))
56   {
57     /*
58       Previous int tells us how many ints the pointer has been shifted from
59       the original address provided by the system malloc().
60     */
61     int shift = *(((int*)ptr)-1) - SHIFT_CLASSID;
62     if (shift > PETSC_MEMALIGN-1) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap");
63     if (shift < 0) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap");
64     ptr = (void*)(((int*)ptr) - shift);
65   }
66 #endif
67 
68 #if defined(PETSC_HAVE_FREE_RETURN_INT)
69   int err = free(ptr);
70   if (err) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"System free returned error %d\n",err);
71 #else
72   free(ptr);
73 #endif
74   return 0;
75 }
76 
77 #undef __FUNCT__
78 #define __FUNCT__ "PetscReallocAlign"
79 PetscErrorCode PetscReallocAlign(size_t mem, int line, const char func[], const char file[], void **result)
80 {
81   PetscErrorCode ierr;
82 
83   if (!mem) {
84     ierr = PetscFreeAlign(*result, line, func, file);
85     if (ierr) return ierr;
86     *result = NULL;
87     return 0;
88   }
89 #if (!(defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) && !defined(PETSC_HAVE_MEMALIGN))
90   {
91     /*
92       Previous int tells us how many ints the pointer has been shifted from
93       the original address provided by the system malloc().
94     */
95     int shift = *(((int*)*result)-1) - SHIFT_CLASSID;
96     if (shift > PETSC_MEMALIGN-1) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap");
97     if (shift < 0) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"Likely memory corruption in heap");
98     *result = (void*)(((int*)*result) - shift);
99   }
100 #endif
101 
102 #if (defined(PETSC_HAVE_DOUBLE_ALIGN_MALLOC) && (PETSC_MEMALIGN == 8)) || defined(PETSC_HAVE_MEMALIGN)
103   *result = realloc(*result, mem);
104 #else
105   {
106     /*
107       malloc space for two extra chunks and shift ptr 1 + enough to get it PetscScalar aligned
108     */
109     int *ptr = (int *) realloc(*result, mem + 2*PETSC_MEMALIGN);
110     if (ptr) {
111       int shift    = (int)(((PETSC_UINTPTR_T) ptr) % PETSC_MEMALIGN);
112       shift        = (2*PETSC_MEMALIGN - shift)/sizeof(int);
113       ptr[shift-1] = shift + SHIFT_CLASSID;
114       ptr         += shift;
115       *result      = (void*)ptr;
116     } else {
117       *result      = NULL;
118     }
119   }
120 #endif
121   if (!*result) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem);
122 #if defined(PETSC_HAVE_MEMALIGN)
123   /* There are no standard guarantees that realloc() maintains the alignment of memalign(), so I think we have to
124    * realloc and, if the alignment is wrong, malloc/copy/free. */
125   if (((size_t) (*result)) % PETSC_MEMALIGN) {
126     void *newResult;
127 
128     newResult = memalign(PETSC_MEMALIGN,mem);
129     if (!newResult) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_MEM,PETSC_ERROR_INITIAL,"Memory requested %.0f",(PetscLogDouble)mem);
130     ierr = PetscMemcpy(newResult,*result,mem);
131     if (ierr) return ierr;
132 #if defined(PETSC_HAVE_FREE_RETURN_INT)
133     {
134       int err = free(*result);
135       if (err) return PetscError(PETSC_COMM_SELF,line,func,file,PETSC_ERR_PLIB,PETSC_ERROR_INITIAL,"System free returned error %d\n",err);
136     }
137 #else
138     free(*result);
139 #endif
140     *result = newResult;
141   }
142 #endif
143   return 0;
144 }
145 
146 PetscErrorCode (*PetscTrMalloc)(size_t,int,const char[],const char[],void**) = PetscMallocAlign;
147 PetscErrorCode (*PetscTrFree)(void*,int,const char[],const char[])           = PetscFreeAlign;
148 PetscErrorCode (*PetscTrRealloc)(size_t,int,const char[],const char[],void**) = PetscReallocAlign;
149 
150 PetscBool petscsetmallocvisited = PETSC_FALSE;
151 
152 #undef __FUNCT__
153 #define __FUNCT__ "PetscMallocSet"
154 /*@C
155    PetscMallocSet - Sets the routines used to do mallocs and frees.
156    This routine MUST be called before PetscInitialize() and may be
157    called only once.
158 
159    Not Collective
160 
161    Input Parameters:
162 +  malloc - the malloc routine
163 -  free - the free routine
164 
165    Level: developer
166 
167    Concepts: malloc
168    Concepts: memory^allocation
169 
170 @*/
171 PetscErrorCode  PetscMallocSet(PetscErrorCode (*imalloc)(size_t,int,const char[],const char[],void**),
172                                               PetscErrorCode (*ifree)(void*,int,const char[],const char[]))
173 {
174   PetscFunctionBegin;
175   if (petscsetmallocvisited && (imalloc != PetscTrMalloc || ifree != PetscTrFree)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"cannot call multiple times");
176   PetscTrMalloc         = imalloc;
177   PetscTrFree           = ifree;
178   petscsetmallocvisited = PETSC_TRUE;
179   PetscFunctionReturn(0);
180 }
181 
182 #undef __FUNCT__
183 #define __FUNCT__ "PetscMallocClear"
184 /*@C
185    PetscMallocClear - Resets the routines used to do mallocs and frees to the
186         defaults.
187 
188    Not Collective
189 
190    Level: developer
191 
192    Notes:
193     In general one should never run a PETSc program with different malloc() and
194     free() settings for different parts; this is because one NEVER wants to
195     free() an address that was malloced by a different memory management system
196 
197 @*/
198 PetscErrorCode  PetscMallocClear(void)
199 {
200   PetscFunctionBegin;
201   PetscTrMalloc         = PetscMallocAlign;
202   PetscTrFree           = PetscFreeAlign;
203   petscsetmallocvisited = PETSC_FALSE;
204   PetscFunctionReturn(0);
205 }
206 
207 #undef __FUNCT__
208 #define __FUNCT__ "PetscMemoryTrace"
209 PetscErrorCode PetscMemoryTrace(const char label[])
210 {
211   PetscErrorCode        ierr;
212   PetscLogDouble        mem,mal;
213   static PetscLogDouble oldmem = 0,oldmal = 0;
214 
215   PetscFunctionBegin;
216   ierr = PetscMemoryGetCurrentUsage(&mem);CHKERRQ(ierr);
217   ierr = PetscMallocGetCurrentUsage(&mal);CHKERRQ(ierr);
218 
219   ierr = PetscPrintf(PETSC_COMM_WORLD,"%s High water  %8.3f MB increase %8.3f MB Current %8.3f MB increase %8.3f MB\n",label,mem*1e-6,(mem - oldmem)*1e-6,mal*1e-6,(mal - oldmal)*1e-6);CHKERRQ(ierr);
220   oldmem = mem;
221   oldmal = mal;
222   PetscFunctionReturn(0);
223 }
224