blocxx
MemTracer.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
3 * Copyright (C) 2006, Novell, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of
14 * Vintela, Inc.,
15 * nor Novell, Inc.,
16 * nor the names of its contributors or employees may be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *******************************************************************************/
32 
33 
39 #define BLOCXX_MEMTRACER_CPP_INCLUDE_GUARD_
40 #include "blocxx/BLOCXX_config.h"
41 #ifdef BLOCXX_DEBUG_MEMORY
42 #include "blocxx/MemTracer.hpp"
43 #include "blocxx/Mutex.hpp"
44 #include <map>
45 #include <cstdio>
46 #include <cstdlib>
47 #include <cassert>
48 #include <errno.h>
49 
50 #ifdef BLOCXX_HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 
54 #define BLOCXX_MEM_SIG 0xaaaaaaaa
55 #define BLOCXX_FREE_MEM_SIG 0xbbbbbbbb
56 
57 namespace BLOCXX_NAMESPACE
58 {
59 
60 // need to use our own allocator to avoid a deadlock with the standard allocator locking with a non-recursive mutex.
61 template <typename T>
62 class MemTracerAllocator
63 {
64 public:
65  typedef std::size_t size_type;
66  typedef std::ptrdiff_t difference_type;
67  typedef T value_type;
68  typedef value_type* pointer;
69  typedef const value_type* const_pointer;
70  typedef value_type& reference;
71  typedef const value_type& const_reference;
72  template <class U> struct rebind
73  {
74  typedef MemTracerAllocator<U> other;
75  };
76 
77  MemTracerAllocator() throw() {}
78  template <class U> MemTracerAllocator(const MemTracerAllocator<U>& other) throw() {}
79  pointer address(reference r) const { return &r; }
80  const_pointer address(const_reference r) const { return &r; }
81  pointer allocate(size_type n, const_pointer hint = 0)
82  {
83  return static_cast<pointer>(::malloc(n));
84  }
85  void deallocate(pointer p, size_type n)
86  {
87  ::free(p);
88  }
89  size_type max_size() const throw() { return static_cast<size_type>(-1); }
90  void construct(pointer p, const_reference val) { new(p) value_type(val); }
91  void destroy(pointer p) { p->~value_type(); }
92 
93 };
94 
95 
96 
97 static const char* const noFile = "<no file>";
98 class MemTracer
99 {
100 public:
101  class Entry
102  {
103  public:
104  Entry (char const * file, int line, size_t sz)
105  : m_file(file), m_line(line), m_size(sz), m_isDeleted(false) {}
106  Entry()
107  : m_file(NULL), m_line(-1), m_size(0), m_isDeleted(false) {}
108  char const* getFile() const { return m_file; }
109  int getLine() const { return m_line; }
110  size_t getSize() const { return m_size; }
111  void setDeleted() { m_isDeleted = true; }
112  bool isDeleted() { return m_isDeleted; }
113  private:
114  char const* m_file;
115  int m_line;
116  size_t m_size;
117  bool m_isDeleted;
118  };
119 private:
120  class Lock
121  {
122  public:
123  Lock(MemTracer & tracer) : m_tracer(tracer) { m_tracer.lock (); }
124  ~Lock()
125  {
126  try
127  {
128  m_tracer.unlock ();
129  }
130  catch (...)
131  {
132  // don't let exceptions escape
133  }
134  }
135  private:
136  MemTracer& m_tracer;
137  };
138  typedef MemTracerAllocator<std::pair<void* const, Entry> > alloc_t;
139  typedef std::map<void*, Entry, std::less<void*>, alloc_t > map_t;
140  typedef map_t::iterator iterator;
141  friend class Lock;
142 public:
143  MemTracer();
144  ~MemTracer();
145  void add(void* p, char const* file, int line, size_t sz);
146  void* remove(void * p);
147  void dump();
148  Entry getEntry(void* idx);
149  void printEntry(void* p);
150  void checkMap();
151 private:
152  void lock() { m_lockCount++; }
153  void unlock() { m_lockCount--; }
154 private:
155  map_t m_map;
156  int m_lockCount;
157 };
158 
159 static Mutex* memguard = NULL;
160 static MemTracer* MemoryTracer = 0;
161 static bool _shuttingDown = false;
162 static bool noFree = false;
163 static bool aggressive = false;
164 static bool disabled = false;
166 void
167 myAtExitFunction()
168 {
169  _shuttingDown = true;
170  if (MemoryTracer != 0)
171  {
172  fprintf(stderr, "*******************************************************************************\n");
173  fprintf(stderr, "* D U M P I N G M E M O R Y\n");
174  fprintf(stderr, "*******************************************************************************\n");
175  MemoryTracer->dump();
176  fprintf(stderr, "-------------------------------------------------------------------------------\n");
177  fprintf(stderr, "- D O N E D U M P I N G M E M O R Y\n");
178  fprintf(stderr, "-------------------------------------------------------------------------------\n");
179  }
180  else
181  {
182  fprintf(stderr, BLOCXX_PACKAGE_NAME": MemoryTracer object does not exist\n");
183  }
184 }
185 static bool owInternal = false;
186 static bool initialized = false;
187 void
188 processEnv()
189 {
190  if (!initialized)
191  {
192  if (getenv("BLOCXX_MEM_DISABLE") && getenv("BLOCXX_MEM_DISABLE")[0] == '1')
193  {
194  disabled = true;
195  }
196  if (getenv("BLOCXX_MEM_NOFREE") && getenv("BLOCXX_MEM_NOFREE")[0] == '1')
197  {
198  noFree = true;
199  }
200  if (getenv("BLOCXX_MEM_AGGRESSIVE") && getenv("BLOCXX_MEM_AGGRESSIVE")[0] == '1')
201  {
202  aggressive = true;
203  fprintf(stderr, "MemTracer running in aggressive mode.\n");
204  }
205  initialized = true;
206  }
207 }
209 void
210 allocMemTracer()
211 {
212  owInternal = true;
213  processEnv();
214  if (!disabled)
215  {
216  if (memguard == 0)
217  {
218  memguard = new Mutex;
219  memguard->acquire();
220  }
221  if (MemoryTracer == 0)
222  {
223  atexit(myAtExitFunction);
224  MemoryTracer = new MemTracer;
225  }
226  }
227  owInternal = false;
228 }
230 void DumpMemory()
231 {
232  if (MemoryTracer != 0)
233  {
234  MemoryTracer->dump();
235  }
236  else
237  {
238  fprintf(stderr, BLOCXX_PACKAGE_NAME": MemoryTracer object does not exist\n");
239  }
240 }
242 MemTracer::MemTracer() : m_lockCount (0)
243 {
244 }
246 MemTracer::~MemTracer()
247 {
248  try
249  {
250  dump();
251  }
252  catch (...)
253  {
254  // don't let exceptions escape
255  }
256 }
257 //static int delCount = 0;
259 static void*
260 checkSigs(void* p, size_t sz)
261 {
262  assert(sz);
263  assert(p);
264  unsigned long* plong = (unsigned long*)((char*)p - 4);
265  if (*plong != BLOCXX_MEM_SIG)
266  {
267  fprintf(stderr, "UNDERRUN: Beginning boundary problem. "
268  "Sig is %x\n", (unsigned int)*plong);
269  MemoryTracer->printEntry(p);
270  assert(0);
271  }
272  plong = (unsigned long*)((char*)p + sz);
273  if (*plong != BLOCXX_MEM_SIG)
274  {
275  fprintf(stderr, "OVERRUN: Ending boundary problem. "
276  "Sig is %x\n", (unsigned int)*plong);
277  MemoryTracer->printEntry(p);
278  fflush(stderr);
279  assert(0);
280  }
281  return (void*)((char*)p - 4);
282 }
284 static void*
285 checkAndSwitchSigs(void* p, size_t sz)
286 {
287  assert(sz);
288  assert(p);
289  unsigned long* plong = (unsigned long*)((char*)p - 4);
290  if (*plong != BLOCXX_MEM_SIG)
291  {
292  fprintf(stderr, "UNDERRUN: Beginning boundary problem. "
293  "Sig is %x\n", (unsigned int)*plong);
294  assert(0);
295  }
296  *plong = BLOCXX_FREE_MEM_SIG;
297  plong = (unsigned long*)((char*)p + sz);
298  if (*plong != BLOCXX_MEM_SIG)
299  {
300  fprintf(stderr, "OVERRUN: Ending boundary problem. "
301  "Sig is %x\n", (unsigned int)*plong);
302  assert(0);
303  }
304  *plong = BLOCXX_FREE_MEM_SIG;
305  return (void*)((char*)p - 4);
306 }
308 void
309 MemTracer::checkMap()
310 {
311  for (iterator it = m_map.begin(); it != m_map.end(); ++it)
312  {
313  if (!it->second.isDeleted())
314  {
315  checkSigs(it->first, it->second.getSize());
316  }
317  }
318 }
320 void
321 MemTracer::add(void* p, char const* file, int line, size_t sz)
322 {
323  const char* pfile = noFile;
324  if (file)
325  {
326  pfile = strdup(file);
327  }
328  m_map[p] = Entry(pfile, line, sz);
329 }
331 void*
332 MemTracer::remove(void* p)
333 {
334  iterator it = m_map.find(p);
335  if (it != m_map.end())
336  {
337  if (noFree)
338  {
339  if (it->second.isDeleted())
340  {
341  fprintf(stderr, "DOUBLE DELETE (NOFREE): Attempting to double "
342  "delete memory at: %p\n", p);
343  assert(0);
344  }
345  }
346  void* ptrToFree = checkAndSwitchSigs(p, it->second.getSize());
347  void* pfile = (void*) it->second.getFile();
348  if (noFree)
349  {
350  it->second.setDeleted();
351  }
352  else
353  {
354  m_map.erase(it);
355  if (pfile != noFile)
356  {
357  free(pfile);
358  }
359  }
360  return ptrToFree;
361  }
362  fprintf(stderr, "Attempting to delete memory not in map: %p\n", p);
363  if (!noFree)
364  {
365  fprintf(stderr, "Trying to check beginning signature...\n");
366  unsigned long* plong = (unsigned long*)((char*)p - 4);
367  if (*plong == BLOCXX_MEM_SIG)
368  {
369  fprintf(stderr, "MemTracer is broken\n");
370  assert(0);
371  }
372  if (*plong == BLOCXX_FREE_MEM_SIG)
373  {
374  fprintf(stderr, "DOUBLE DELETE: This memory was previously freed by MemTracer, "
375  "probably double delete\n");
376  assert(0);
377  }
378  fprintf(stderr, "No signature detected.\n");
379  }
380  fprintf(stderr, "UNKNOWN ADDRESS\n");
381  assert(0);
382  return p;
383 }
385 void
386 MemTracer::printEntry(void* p)
387 {
388  Entry entry = getEntry(p);
389  fprintf(stderr, "\tFILE: %s", entry.getFile());
390  fprintf(stderr, "\tLINE: %d", entry.getLine());
391  fprintf(stderr, "\tSIZE: %lu", static_cast<unsigned long>(entry.getSize()));
392  fprintf(stderr, "\tADDR: %p\n", p);
393 }
395 MemTracer::Entry
396 MemTracer::getEntry(void* idx)
397 {
398  memguard->acquire();
399  iterator it = m_map.find(idx);
400  MemTracer::Entry rval;
401  if (it != m_map.end())
402  {
403  rval = it->second;
404  }
405  memguard->release();
406  return rval;
407 }
409 void
410 MemTracer::dump()
411 {
412  memguard->acquire();
413  if (m_map.size() != 0)
414  {
415  fprintf(stderr, "**** %lu MEMORY LEAK(S) DETECTED\n", static_cast<unsigned long>(m_map.size()));
416  size_t total = 0;
417  for (iterator it = m_map.begin(); it != m_map.end (); ++it)
418  {
419  if (!it->second.isDeleted())
420  {
421  fprintf(stderr, "\tFILE: %s", it->second.getFile());
422  fprintf(stderr, "\tLINE: %d", it->second.getLine());
423  fprintf(stderr, "\tSIZE: %lu", static_cast<unsigned long>(it->second.getSize()));
424  fprintf(stderr, "\tADDR: %p\n", it->first);
425  total += it->second.getSize();
426  }
427  }
428  fprintf(stderr, "***** END MEMORY LEAKS - TOTAL MEMORY LEAKED = %lu\n", static_cast<unsigned long>(total));
429  }
430  memguard->release();
431 }
433 static void
434 writeSigs(void *& p, size_t size)
435 {
436  unsigned long* plong = (unsigned long*)p;
437  *plong = BLOCXX_MEM_SIG;
438  plong = (unsigned long*)((char*)p + size + 4);
439  *plong = BLOCXX_MEM_SIG;
440  p = (void*)((char*)p + 4);
441 }
442 static int internalNewCount = 0;
444 static void*
445 doNew(size_t size, char const* file, int line)
446 {
447  if (memguard)
448  {
449  memguard->acquire();
450  }
451  if (owInternal || disabled)
452  {
453  ++internalNewCount;
454  if (internalNewCount > 2 && !disabled)
455  {
456  fprintf(stderr, "INTERNAL NEW called more than twice! "
457  "Possible bug in MemTracer.\n");
458  assert(0);
459  }
460  void* rval = malloc(size);
461  if (memguard)
462  {
463  memguard->release();
464  }
465  --internalNewCount;
466  return rval;
467  }
468  allocMemTracer();
469  if (disabled)
470  {
471  return malloc(size);
472  }
473  if (aggressive)
474  {
475  MemoryTracer->checkMap();
476  }
477  void* p = malloc(size + 8);
478  if (!p)
479  {
480  memguard->release();
481  perror("malloc failed.");
482  exit(errno);
483  }
484  writeSigs(p, size);
485  owInternal = true;
486  assert (MemoryTracer);
487  MemoryTracer->add(p, file, line, size);
488  owInternal = false;
489  memguard->release();
490  return p;
491 }
493 static void
494 doDelete(void* p)
495 {
496  if (p)
497  {
498  if (memguard)
499  {
500  memguard->acquire();
501  }
502  if (owInternal || disabled)
503  {
504  if (!disabled)
505  {
506  fprintf(stderr, "INTERNAL DELETE: %p\n", p);
507  }
508  free(p);
509  if (memguard)
510  {
511  memguard->release();
512  }
513  return;
514  }
515  if (aggressive)
516  {
517  MemoryTracer->checkMap();
518  }
519  owInternal = true;
520  if (MemoryTracer != 0)
521  {
522  p = MemoryTracer->remove((void*)((char*)p));
523  }
524  else
525  {
526  printf("** MemTracer can't remove delete from map: ADDR: %p\n", p);
527  }
528  if (!noFree)
529  {
530  free(p);
531  }
532  owInternal = false;
533  memguard->release();
534  }
535  if (_shuttingDown)
536  {
537  memguard->release();
538  fprintf(stderr, "delete called\n");
539  }
540 }
541 
542 } // end namespace BLOCXX_NAMESPACE
543 
545 void*
546 operator new[](std::size_t size, char const* file, int line) throw(std::bad_alloc)
547 {
548  return BLOCXX_NAMESPACE::doNew(size, file, line);
549 }
551 void*
552 operator new(std::size_t size, char const* file, int line) throw(std::bad_alloc)
553 {
554  return BLOCXX_NAMESPACE::doNew(size, file, line);
555 }
557 void*
558 operator new[](std::size_t size) throw(std::bad_alloc)
559 {
560  return BLOCXX_NAMESPACE::doNew(size, NULL, 0);
561 }
563 void*
564 operator new(std::size_t size) throw(std::bad_alloc)
565 {
566  return BLOCXX_NAMESPACE::doNew(size, NULL, 0);
567 }
568 void
569 operator delete(void* p)
570 {
571  BLOCXX_NAMESPACE::doDelete(p);
572 }
573 void
574 operator delete[](void* p)
575 {
576  BLOCXX_NAMESPACE::doDelete(p);
577 }
578 
579 
580 #endif // BLOCXX_DEBUG_MEMORY
581 
582