1 /* Copyright (C) 1999-2020 Massachusetts Institute of Technology.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 /* Debugging versions of malloc & free, to help catch common errors.
19    The routines in this file were adapted from similar routines in the
20    FFTW library (www.fftw.org), written by Matteo Frigo and Steven
21    G. Johnson.  FFTW is also GPLed and is also copyrighted by MIT.  */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 
26 #include "config.h"
27 
28 #ifdef HAVE_MALLOC_H
29 #include <malloc.h>
30 #endif
31 
32 #include "mpi_utils.h"
33 #include "check.h"
34 
35 void* (*my_malloc_hook) (size_t) = 0;
36 
37 /**********************************************************
38  *   DEBUGGING CODE
39  **********************************************************/
40 
41 #ifdef DEBUG
42 
check_breakpoint(void)43 void check_breakpoint(void)
44 {
45      /* this function is only here so that we can drop into a breakpoint
46 	in a debugger when a CHECK macro fails... */
47 }
48 
49 #endif
50 
51 #ifdef DEBUG_MALLOC
52 
53 #  undef malloc
54 #  undef free
55 
56 /*
57  * debugging malloc/free.  Initialize every malloced and freed area to
58  * random values, just to make sure we are not using uninitialized
59  * pointers.  Also check for writes past the ends of allocated blocks,
60  * and a couple of other things.
61  *
62  */
63 
64 static int debug_malloc_cnt = 0;
65 static int debug_malloc_total = 0;
66 
67 #define MAGIC 0xABadCafe
68 #define MMAGIC (((int) MAGIC) < 0 ? ((int) MAGIC) : -((int) MAGIC))
69 #define PAD_FACTOR 2
70 #define TWOINTS (2 * sizeof(int))
71 
72 #define VERBOSE_ALLOCATION 0
73 
74 #if VERBOSE_ALLOCATION
75 #define WHEN_VERBOSE(a) a
76 #else
77 #define WHEN_VERBOSE(a)
78 #endif
79 
debug_malloc(size_t n)80 void *debug_malloc(size_t n)
81 {
82      char *p;
83      int i;
84 
85      WHEN_VERBOSE(mpi_one_fprintf(stderr,"DEBUG_MALLOC %d\n", n));
86 
87      if (n == 0)
88 	  mpi_one_fprintf(stderr, "(Allocating a block of zero size.)\n");
89 
90      debug_malloc_total += n;
91 
92      p = (char *) malloc(PAD_FACTOR * n + TWOINTS);
93      CHECK(p, "debug_malloc: out of memory\n");
94 
95      /* store the size in a known position */
96      ((int *) p)[0] = n;
97      ((int *) p)[1] = MAGIC;
98      for (i = 0; i < PAD_FACTOR * n; ++i)
99 	  p[i + TWOINTS] = (char) (i ^ 0xDEADBEEF);
100 
101      ++debug_malloc_cnt;
102 
103      /* skip the size we stored previously */
104      return (void *) (p + TWOINTS);
105 }
106 
debug_free(void * p)107 void debug_free(void *p)
108 {
109      char *q = ((char *) p) - TWOINTS;
110 
111      CHECK(p, "debug_free: tried to free NULL pointer!\n");
112      CHECK(q, "debug_free: tried to free NULL+TWOINTS pointer!\n");
113 
114      {
115 	  int n = ((int *) q)[0];
116 	  int magic = ((int *) q)[1];
117 	  int i;
118 
119 	  WHEN_VERBOSE(mpi_one_fprintf(stderr,"DEBUG_FREE %d\n", n));
120 	  CHECK(n != MMAGIC, "Tried to free a freed pointer!\n");
121 	  *((int *) q) = MMAGIC; /* to detect duplicate free's */
122 
123 	  CHECK(magic == MAGIC, "Wrong magic in debug_free()!\n");
124 	  ((int *) q)[1] = ~MAGIC;
125 
126 	  CHECK(n >= 0, "Tried to free block with corrupt size descriptor!\n");
127 
128 	  debug_malloc_total -= n;
129 
130 	  CHECK(debug_malloc_total >= 0,
131 		"debug_malloc_total went negative!\n");
132 
133 	  /* check for writing past end of array: */
134 	  for (i = n; i < PAD_FACTOR * n; ++i)
135 	       if (q[i + TWOINTS] != (char) (i ^ 0xDEADBEEF))
136 		    mpi_die("Byte %d past end of array has changed!\n"
137 			    "Array bounds overwritten!\n",
138 			    i - n + 1);
139 	  for (i = 0; i < PAD_FACTOR * n; ++i)
140 	       q[i + TWOINTS] = (char) (i ^ 0xBEEFDEAD);
141 
142 	  --debug_malloc_cnt;
143 	  free(q);
144      }
145 }
146 
147 #endif /* DEBUG */
148 
149 /* output current memory usage: */
debug_output_malloc_count(void)150 void debug_output_malloc_count(void)
151 {
152 #ifdef DEBUG_MALLOC
153      mpi_one_fprintf(stderr, "malloc: %d blocks, %g kB\n",
154 		     debug_malloc_cnt, debug_malloc_total / 1024.0);
155 #endif
156 }
157 
158 /* check for memory leaks when debugging */
debug_check_memory_leaks(void)159 void debug_check_memory_leaks(void)
160 {
161 #ifdef DEBUG_MALLOC
162      if (debug_malloc_cnt || debug_malloc_total)
163 	  mpi_die("MEMORY LEAK!!!\n"
164 		  "number of unbalanced malloc calls = %d\n"
165 		  "total leaked bytes = %d\n",
166 		  debug_malloc_cnt,
167 		  debug_malloc_total);
168 #endif
169 }
170