STXXL  1.4-dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
malloc_count.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * lib/utils/malloc_count.cpp
3  *
4  * light-weight malloc()-based heap allocation counter based on overriding
5  * malloc/free/realloc() functions.
6  *
7  * See http://panthema.net/2013/malloc_count/ for a tutorial.
8  *
9  * Part of the STXXL. See http://stxxl.sourceforge.net
10  *
11  * Copyright (C) 2013-2015 Timo Bingmann <[email protected]>
12  *
13  * Distributed under the Boost Software License, Version 1.0.
14  * (See accompanying file LICENSE_1_0.txt or copy at
15  * http://www.boost.org/LICENSE_1_0.txt)
16  **************************************************************************/
17 
18 #ifndef _GNU_SOURCE
19 #define _GNU_SOURCE
20 #endif
21 #include <cstdlib>
22 #include <cstring>
23 #include <cstdio>
24 #include <clocale>
25 #include <dlfcn.h>
26 
27 #include <stxxl/bits/config.h>
29 
30 /* user-defined options for output malloc()/free() operations to stderr */
31 
32 static const int log_operations = 1; /* <-- set this to 1 for log output */
33 static const size_t log_operations_threshold = 1024 * 1024;
34 
35 /* option to use gcc's intrinsics to do thread-safe statistics operations */
36 #define THREAD_SAFE_GCC_INTRINSICS STXXL_HAVE_SYNC_ADD_AND_FETCH
37 
38 /* to each allocation additional data is added for bookkeeping. due to
39  * alignment requirements, we can optionally add more than just one integer. */
40 static const size_t alignment = 16; /* bytes (>= 2*sizeof(size_t)) */
41 
42 /* function pointer to the real procedures, loaded using dlsym */
43 typedef void* (* malloc_type)(size_t);
44 typedef void (* free_type)(void*);
45 typedef void* (* realloc_type)(void*, size_t);
46 
47 static malloc_type real_malloc = NULL;
48 static free_type real_free = NULL;
49 static realloc_type real_realloc = NULL;
50 
51 /* a sentinel value prefixed to each allocation */
52 static const size_t sentinel = 0xDEADC0DE;
53 
54 /* a simple memory heap for allocations prior to dlsym loading */
55 #define INIT_HEAP_SIZE 1024 * 1024
57 static size_t init_heap_use = 0;
58 static const int log_operations_init_heap = 0;
59 
60 /* output */
61 #define PPREFIX "malloc_count ### "
62 
63 /*****************************************/
64 /* run-time memory allocation statistics */
65 /*****************************************/
66 
67 static long long peak = 0, curr = 0, total = 0;
68 
70 static void* callback_cookie = NULL;
71 
72 /* add allocation to statistics */
73 static void inc_count(size_t inc)
74 {
75 #if THREAD_SAFE_GCC_INTRINSICS
76  long long mycurr = __sync_add_and_fetch(&curr, inc);
77  if (mycurr > peak) peak = mycurr;
78  total += inc;
79  if (callback) callback(callback_cookie, mycurr);
80 #else
81  if ((curr += inc) > peak) peak = curr;
82  total += inc;
84 #endif
85 }
86 
87 /* decrement allocation to statistics */
88 static void dec_count(size_t dec)
89 {
90 #if THREAD_SAFE_GCC_INTRINSICS
91  long long mycurr = __sync_sub_and_fetch(&curr, dec);
92  if (callback) callback(callback_cookie, mycurr);
93 #else
94  curr -= dec;
96 #endif
97 }
98 
99 /* user function to return the currently allocated amount of memory */
100 extern size_t malloc_count_current(void)
101 {
102  return curr;
103 }
104 
105 /* user function to return the peak allocation */
106 extern size_t malloc_count_peak(void)
107 {
108  return peak;
109 }
110 
111 /* user function to reset the peak allocation to current */
112 extern void malloc_count_reset_peak(void)
113 {
114  peak = curr;
115 }
116 
117 /* user function which prints current and peak allocation to stderr */
118 extern void malloc_count_print_status(void)
119 {
120  fprintf(stderr, PPREFIX "current %'lld, peak %'lld\n",
121  curr, peak);
122 }
123 
124 /* user function to supply a memory profile callback */
126  void* cookie)
127 {
128  callback = cb;
129  callback_cookie = cookie;
130 }
131 
132 /****************************************************/
133 /* exported symbols that overlay the libc functions */
134 /****************************************************/
135 
136 /* exported malloc symbol that overrides loading from libc */
137 extern void * malloc(size_t size) throw ()
138 {
139  void* ret;
140 
141  if (size == 0) return NULL;
142 
143  if (real_malloc)
144  {
145  /* call read malloc procedure in libc */
146  ret = (*real_malloc)(alignment + size);
147 
148  inc_count(size);
149  if (log_operations && size >= log_operations_threshold) {
150  fprintf(stderr, PPREFIX "malloc(%'lld) = %p (current %'lld)\n",
151  (long long)size, (char*)ret + alignment, curr);
152  }
153 
154  /* prepend allocation size and check sentinel */
155  *(size_t*)ret = size;
156  *(size_t*)((char*)ret + alignment - sizeof(size_t)) = sentinel;
157 
158  return (char*)ret + alignment;
159  }
160  else
161  {
162  if (init_heap_use + alignment + size > INIT_HEAP_SIZE) {
163  fprintf(stderr, PPREFIX "init heap full !!!\n");
164  exit(EXIT_FAILURE);
165  }
166 
167  ret = init_heap + init_heap_use;
168  init_heap_use += alignment + size;
169 
170  /* prepend allocation size and check sentinel */
171  *(size_t*)ret = size;
172  *(size_t*)((char*)ret + alignment - sizeof(size_t)) = sentinel;
173 
175  fprintf(stderr, PPREFIX "malloc(%'lld) = %p on init heap\n",
176  (long long)size, (char*)ret + alignment);
177  }
178 
179  return (char*)ret + alignment;
180  }
181 }
182 
183 /* exported free symbol that overrides loading from libc */
184 extern void free(void* ptr) throw ()
185 {
186  size_t size;
187 
188  if (!ptr) return; /* free(NULL) is no operation */
189 
190  if ((char*)ptr >= init_heap &&
191  (char*)ptr <= init_heap + init_heap_use)
192  {
194  fprintf(stderr, PPREFIX "free(%p) on init heap\n", ptr);
195  }
196  return;
197  }
198 
199  if (!real_free) {
200  fprintf(stderr, PPREFIX
201  "free(%p) outside init heap and without real_free !!!\n", ptr);
202  return;
203  }
204 
205  ptr = (char*)ptr - alignment;
206 
207  if (*(size_t*)((char*)ptr + alignment - sizeof(size_t)) != sentinel) {
208  fprintf(stderr, PPREFIX
209  "free(%p) has no sentinel !!! memory corruption?\n", ptr);
210  }
211 
212  size = *(size_t*)ptr;
213  dec_count(size);
214 
215  if (log_operations && size >= log_operations_threshold) {
216  fprintf(stderr, PPREFIX "free(%p) -> %'lld (current %'lld)\n",
217  ptr, (long long)size, curr);
218  }
219 
220  (*real_free)(ptr);
221 }
222 
223 /* exported calloc() symbol that overrides loading from libc, implemented using
224  * our malloc */
225 extern void * calloc(size_t nmemb, size_t size) throw ()
226 {
227  void* ret;
228  size *= nmemb;
229  if (!size) return NULL;
230  ret = malloc(size);
231  memset(ret, 0, size);
232  return ret;
233 }
234 
235 /* exported realloc() symbol that overrides loading from libc */
236 extern void * realloc(void* ptr, size_t size) throw ()
237 {
238  void* newptr;
239  size_t oldsize;
240 
241  if ((char*)ptr >= (char*)init_heap &&
242  (char*)ptr <= (char*)init_heap + init_heap_use)
243  {
245  fprintf(stderr, PPREFIX "realloc(%p) = on init heap\n", ptr);
246  }
247 
248  ptr = (char*)ptr - alignment;
249 
250  if (*(size_t*)((char*)ptr + alignment - sizeof(size_t)) != sentinel) {
251  fprintf(stderr, PPREFIX
252  "realloc(%p) has no sentinel !!! memory corruption?\n",
253  ptr);
254  }
255 
256  oldsize = *(size_t*)ptr;
257 
258  if (oldsize >= size) {
259  /* keep old area, just reduce the size */
260  *(size_t*)ptr = size;
261  return (char*)ptr + alignment;
262  }
263  else {
264  /* allocate new area and copy data */
265  ptr = (char*)ptr + alignment;
266  newptr = malloc(size);
267  memcpy(newptr, ptr, oldsize);
268  free(ptr);
269  return newptr;
270  }
271  }
272 
273  if (size == 0) { /* special case size == 0 -> free() */
274  free(ptr);
275  return NULL;
276  }
277 
278  if (ptr == NULL) { /* special case ptr == 0 -> malloc() */
279  return malloc(size);
280  }
281 
282  ptr = (char*)ptr - alignment;
283 
284  if (*(size_t*)((char*)ptr + alignment - sizeof(size_t)) != sentinel) {
285  fprintf(stderr, PPREFIX
286  "free(%p) has no sentinel !!! memory corruption?\n", ptr);
287  }
288 
289  oldsize = *(size_t*)ptr;
290 
291  dec_count(oldsize);
292  inc_count(size);
293 
294  newptr = (*real_realloc)(ptr, alignment + size);
295 
297  {
298  if (newptr == ptr)
299  fprintf(stderr, PPREFIX
300  "realloc(%'lld -> %'lld) = %p (current %'lld)\n",
301  (long long)oldsize, (long long)size, newptr, curr);
302  else
303  fprintf(stderr, PPREFIX
304  "realloc(%'lld -> %'lld) = %p -> %p (current %'lld)\n",
305  (long long)oldsize, (long long)size, ptr, newptr, curr);
306  }
307 
308  *(size_t*)newptr = size;
309 
310  return (char*)newptr + alignment;
311 }
312 
313 static __attribute__ ((constructor)) void init(void)
314 {
315  char* error;
316 
317  setlocale(LC_NUMERIC, ""); /* for better readable numbers */
318 
319  dlerror();
320 
321  real_malloc = (malloc_type)dlsym(RTLD_NEXT, "malloc");
322  if ((error = dlerror()) != NULL) {
323  fprintf(stderr, PPREFIX "error %s\n", error);
324  exit(EXIT_FAILURE);
325  }
326 
327  real_realloc = (realloc_type)dlsym(RTLD_NEXT, "realloc");
328  if ((error = dlerror()) != NULL) {
329  fprintf(stderr, PPREFIX "error %s\n", error);
330  exit(EXIT_FAILURE);
331  }
332 
333  real_free = (free_type)dlsym(RTLD_NEXT, "free");
334  if ((error = dlerror()) != NULL) {
335  fprintf(stderr, PPREFIX "error %s\n", error);
336  exit(EXIT_FAILURE);
337  }
338 }
339 
340 static __attribute__ ((destructor)) void finish(void)
341 {
342  fprintf(stderr, PPREFIX
343  "exiting, total: %'lld, peak: %'lld, current: %'lld\n",
344  total, peak, curr);
345 }
346 
347 /*****************************************************************************/
#define PPREFIX
void * malloc(size_t size)
void *(* realloc_type)(void *, size_t)
void malloc_count_reset_peak(void)
void *(* malloc_type)(size_t)
static realloc_type real_realloc
static size_t init_heap_use
void * realloc(void *ptr, size_t size)
static void inc_count(size_t inc)
static malloc_count_callback_type callback
static void * callback_cookie
static free_type real_free
void(* free_type)(void *)
size_t malloc_count_current(void)
static long long curr
#define INIT_HEAP_SIZE
static long long peak
void malloc_count_set_callback(malloc_count_callback_type cb, void *cookie)
static const size_t alignment
void malloc_count_print_status(void)
void(* malloc_count_callback_type)(void *cookie, size_t current)
Definition: malloc_count.h:39
static void dec_count(size_t dec)
void * calloc(size_t nmemb, size_t size)
void free(void *ptr)
static long long total
static const size_t log_operations_threshold
static malloc_type real_malloc
static const size_t sentinel
static const int log_operations_init_heap
size_t malloc_count_peak(void)
static char init_heap[1024 *1024]
static const int log_operations