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
57namespace 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.
61template <typename T>
62class MemTracerAllocator
63{
64public:
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
97static const char* const noFile = "<no file>";
98class MemTracer
99{
100public:
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 };
119private:
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;
142public:
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();
151private:
152 void lock() { m_lockCount++; }
153 void unlock() { m_lockCount--; }
154private:
155 map_t m_map;
156 int m_lockCount;
157};
158
159static Mutex* memguard = NULL;
160static MemTracer* MemoryTracer = 0;
161static bool _shuttingDown = false;
162static bool noFree = false;
163static bool aggressive = false;
164static bool disabled = false;
166void
167myAtExitFunction()
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}
185static bool owInternal = false;
186static bool initialized = false;
187void
188processEnv()
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}
209void
210allocMemTracer()
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}
230void 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}
242MemTracer::MemTracer() : m_lockCount (0)
243{
244}
246MemTracer::~MemTracer()
247{
248 try
249 {
250 dump();
251 }
252 catch (...)
253 {
254 // don't let exceptions escape
255 }
256}
257//static int delCount = 0;
259static void*
260checkSigs(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}
284static void*
285checkAndSwitchSigs(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}
308void
309MemTracer::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}
320void
321MemTracer::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}
331void*
332MemTracer::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}
385void
386MemTracer::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}
395MemTracer::Entry
396MemTracer::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}
409void
410MemTracer::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}
433static void
434writeSigs(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}
442static int internalNewCount = 0;
444static void*
445doNew(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}
493static void
494doDelete(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
545void*
546operator new[](std::size_t size, char const* file, int line) throw(std::bad_alloc)
547{
548 return BLOCXX_NAMESPACE::doNew(size, file, line);
549}
551void*
552operator new(std::size_t size, char const* file, int line) throw(std::bad_alloc)
553{
554 return BLOCXX_NAMESPACE::doNew(size, file, line);
555}
557void*
558operator new[](std::size_t size) throw(std::bad_alloc)
559{
560 return BLOCXX_NAMESPACE::doNew(size, NULL, 0);
561}
563void*
564operator new(std::size_t size) throw(std::bad_alloc)
565{
566 return BLOCXX_NAMESPACE::doNew(size, NULL, 0);
567}
568void
569operator delete(void* p)
570{
571 BLOCXX_NAMESPACE::doDelete(p);
572}
573void
574operator delete[](void* p)
575{
576 BLOCXX_NAMESPACE::doDelete(p);
577}
578
579
580#endif // BLOCXX_DEBUG_MEMORY
581
582
Taken from RFC 1321.