source: box/trunk/lib/common/DebugMemLeakFinder.cpp @ 2493

Revision 2493, 11.6 KB checked in by chris, 3 years ago (diff)

gcc 4.3 and 4.4 compile fixes, thanks to Reinhard Tartler and the Debian
project. See also:

 http://patch-tracking.debian.net/package/boxbackup/0.11~rc2+r2072-1
 http://lists.warhead.org.uk/pipermail/boxbackup/2009-April/005159.html

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    MemLeakFinder.cpp
5//              Purpose: Memory leak finder
6//              Created: 12/1/04
7//
8// --------------------------------------------------------------------------
9
10
11#ifndef BOX_RELEASE_BUILD
12
13#include "Box.h"
14
15#undef malloc
16#undef realloc
17#undef free
18
19#ifdef HAVE_UNISTD_H
20        #include <unistd.h>
21#endif
22
23#include <map>
24#include <stdio.h>
25#include <string.h>
26#include <set>
27#include <cstdlib> // for std::atexit
28
29#include "MemLeakFinder.h"
30
31static bool memleakfinder_initialised = false;
32bool memleakfinder_global_enable = false;
33
34typedef struct
35{
36        size_t size;
37        const char *file;
38        int line;
39} MallocBlockInfo;
40
41typedef struct
42{
43        size_t size;
44        const char *file;
45        int line;
46        bool array;
47} ObjectInfo;
48
49namespace
50{
51        static std::map<void *, MallocBlockInfo> sMallocBlocks;
52        static std::map<void *, ObjectInfo> sObjectBlocks;
53        static bool sTrackingDataDestroyed = false;
54
55        static class DestructionWatchdog
56        {
57                public:
58                ~DestructionWatchdog()
59                {
60                        sTrackingDataDestroyed = true;
61                }
62        }
63        sWatchdog;
64       
65        static bool sTrackMallocInSection = false;
66        static std::set<void *> sSectionMallocBlocks;
67        static bool sTrackObjectsInSection = false;
68        static std::map<void *, ObjectInfo> sSectionObjectBlocks;
69       
70        static std::set<void *> sNotLeaks;
71
72        void *sNotLeaksPre[1024];
73        size_t sNotLeaksPreNum = 0;
74}
75
76void memleakfinder_init()
77{
78        ASSERT(!memleakfinder_initialised);
79
80        {
81                // allocates a permanent buffer on Solaris.
82                // not a leak?
83                std::ostringstream oss;
84        }
85
86        memleakfinder_initialised = true;
87}
88
89MemLeakSuppressionGuard::MemLeakSuppressionGuard()
90{
91        ASSERT(memleakfinder_global_enable);
92        memleakfinder_global_enable = false;
93}
94
95MemLeakSuppressionGuard::~MemLeakSuppressionGuard()
96{
97        ASSERT(!memleakfinder_global_enable);
98        memleakfinder_global_enable = true;
99}
100
101// these functions may well allocate memory, which we don't want to track.
102static int sInternalAllocDepth = 0;
103
104class InternalAllocGuard
105{
106        public:
107        InternalAllocGuard () { sInternalAllocDepth++; }
108        ~InternalAllocGuard() { sInternalAllocDepth--; }
109};
110
111void memleakfinder_malloc_add_block(void *b, size_t size, const char *file, int line)
112{
113        InternalAllocGuard guard;
114
115        if(b != 0)
116        {
117                MallocBlockInfo i;
118                i.size = size;
119                i.file = file;
120                i.line = line;
121                sMallocBlocks[b] = i;
122               
123                if(sTrackMallocInSection)
124                {
125                        sSectionMallocBlocks.insert(b);
126                }
127        }
128}
129
130void *memleakfinder_malloc(size_t size, const char *file, int line)
131{
132        InternalAllocGuard guard;
133
134        void *b = std::malloc(size);
135        if(!memleakfinder_global_enable) return b;
136        if(!memleakfinder_initialised)   return b;
137
138        memleakfinder_malloc_add_block(b, size, file, line);
139
140        //TRACE4("malloc(), %d, %s, %d, %08x\n", size, file, line, b);
141        return b;
142}
143
144void *memleakfinder_realloc(void *ptr, size_t size)
145{
146        InternalAllocGuard guard;
147
148        if(!memleakfinder_global_enable || !memleakfinder_initialised)
149        {
150                return std::realloc(ptr, size);
151        }
152
153        // Check it's been allocated
154        std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr));
155        if(ptr && i == sMallocBlocks.end())
156        {
157                BOX_WARNING("Block " << ptr << " realloc()ated, but not "
158                        "in list. Error? Or allocated in startup static "
159                        "objects?");
160        }
161
162        void *b = std::realloc(ptr, size);
163
164        if(ptr && i!=sMallocBlocks.end())
165        {
166                // Worked?
167                if(b != 0)
168                {
169                        // Update map
170                        MallocBlockInfo inf = i->second;
171                        inf.size = size;
172                        sMallocBlocks.erase(i);
173                        sMallocBlocks[b] = inf;
174
175                        if(sTrackMallocInSection)
176                        {
177                                std::set<void *>::iterator si(sSectionMallocBlocks.find(ptr));
178                                if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si);
179                                sSectionMallocBlocks.insert(b);
180                        }
181                }
182        }
183        else
184        {
185                memleakfinder_malloc_add_block(b, size, "FOUND-IN-REALLOC", 0);
186        }
187
188        //TRACE3("realloc(), %d, %08x->%08x\n", size, ptr, b);
189        return b;
190}
191
192void memleakfinder_free(void *ptr)
193{
194        InternalAllocGuard guard;
195
196        if(memleakfinder_global_enable && memleakfinder_initialised)
197        {
198                // Check it's been allocated
199                std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr));
200                if(i != sMallocBlocks.end())
201                {
202                        sMallocBlocks.erase(i);
203                }
204                else
205                {
206                        BOX_WARNING("Block " << ptr << " freed, but not "
207                                "known. Error? Or allocated in startup "
208                                "static allocation?");
209                }
210
211                if(sTrackMallocInSection)
212                {
213                        std::set<void *>::iterator si(sSectionMallocBlocks.find(ptr));
214                        if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si);
215                }
216        }
217
218        //TRACE1("free(), %08x\n", ptr);
219        std::free(ptr);
220}
221
222
223void memleakfinder_notaleak_insert_pre()
224{
225        InternalAllocGuard guard;
226
227        if(!memleakfinder_global_enable) return;
228        if(!memleakfinder_initialised)   return;
229
230        for(size_t l = 0; l < sNotLeaksPreNum; l++)
231        {
232                sNotLeaks.insert(sNotLeaksPre[l]);
233        }
234
235        sNotLeaksPreNum = 0;
236}
237
238bool is_leak(void *ptr)
239{
240        InternalAllocGuard guard;
241
242        ASSERT(memleakfinder_initialised);
243        memleakfinder_notaleak_insert_pre();
244        return sNotLeaks.find(ptr) == sNotLeaks.end();
245}
246
247void memleakfinder_notaleak(void *ptr)
248{
249        InternalAllocGuard guard;
250
251        ASSERT(!sTrackingDataDestroyed);
252
253        memleakfinder_notaleak_insert_pre();
254        if(memleakfinder_global_enable && memleakfinder_initialised)
255        {
256                sNotLeaks.insert(ptr);
257        }
258        else
259        {
260                if ( sNotLeaksPreNum < 
261                         sizeof(sNotLeaksPre)/sizeof(*sNotLeaksPre) )
262                        sNotLeaksPre[sNotLeaksPreNum++] = ptr;
263        }
264/*      {
265                std::map<void *, MallocBlockInfo>::iterator i(sMallocBlocks.find(ptr));
266                if(i != sMallocBlocks.end()) sMallocBlocks.erase(i);
267        }
268        {
269                std::set<void *>::iterator si(sSectionMallocBlocks.find(ptr));
270                if(si != sSectionMallocBlocks.end()) sSectionMallocBlocks.erase(si);
271        }
272        {
273                std::map<void *, ObjectInfo>::iterator i(sObjectBlocks.find(ptr));
274                if(i != sObjectBlocks.end()) sObjectBlocks.erase(i);
275        }*/
276}
277
278
279
280// start monitoring a section of code
281void memleakfinder_startsectionmonitor()
282{
283        InternalAllocGuard guard;
284
285        ASSERT(memleakfinder_initialised);
286        ASSERT(!sTrackingDataDestroyed);
287
288        sTrackMallocInSection = true;
289        sSectionMallocBlocks.clear();
290        sTrackObjectsInSection = true;
291        sSectionObjectBlocks.clear();
292}
293
294// trace all blocks allocated and still allocated since memleakfinder_startsectionmonitor() called
295void memleakfinder_traceblocksinsection()
296{
297        InternalAllocGuard guard;
298
299        ASSERT(memleakfinder_initialised);
300        ASSERT(!sTrackingDataDestroyed);
301
302        std::set<void *>::iterator s(sSectionMallocBlocks.begin());
303        for(; s != sSectionMallocBlocks.end(); ++s)
304        {
305                std::map<void *, MallocBlockInfo>::const_iterator i(sMallocBlocks.find(*s));
306                if(i == sMallocBlocks.end())
307                {
308                        BOX_WARNING("Logical error in section block finding");
309                }
310                else
311                {
312                        BOX_TRACE("Block " << i->first << " size " <<
313                                i->second.size << " allocated at " <<
314                                i->second.file << ":" << i->second.line);
315                }
316        }
317        for(std::map<void *, ObjectInfo>::const_iterator i(sSectionObjectBlocks.begin()); i != sSectionObjectBlocks.end(); ++i)
318        {
319                BOX_TRACE("Object" << (i->second.array?" []":"") << " " <<
320                        i->first << " size " << i->second.size <<
321                        " allocated at " << i->second.file << 
322                        ":" << i->second.line);
323        }
324}
325
326int memleakfinder_numleaks()
327{
328        InternalAllocGuard guard;
329
330        ASSERT(memleakfinder_initialised);
331        ASSERT(!sTrackingDataDestroyed);
332
333        int n = 0;
334       
335        for(std::map<void *, MallocBlockInfo>::const_iterator i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i)
336        {
337                if(is_leak(i->first)) ++n;
338        }
339       
340        for(std::map<void *, ObjectInfo>::const_iterator i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i)
341        {
342                const ObjectInfo& rInfo = i->second;
343                if(is_leak(i->first)) ++n;
344        }
345
346        return n;
347}
348
349void memleakfinder_reportleaks_file(FILE *file)
350{
351        InternalAllocGuard guard;
352
353        ASSERT(!sTrackingDataDestroyed);
354
355        for(std::map<void *, MallocBlockInfo>::const_iterator
356                i(sMallocBlocks.begin()); i != sMallocBlocks.end(); ++i)
357        {
358                if(is_leak(i->first))
359                {
360                        ::fprintf(file, "Block %p size %d allocated at "
361                                "%s:%d\n", i->first, i->second.size,
362                                i->second.file, i->second.line);
363                }
364        }
365
366        for(std::map<void *, ObjectInfo>::const_iterator
367                i(sObjectBlocks.begin()); i != sObjectBlocks.end(); ++i)
368        {
369                if(is_leak(i->first))
370                {
371                        ::fprintf(file, "Object%s %p size %d allocated at "
372                                "%s:%d\n", i->second.array?" []":"",
373                                i->first, i->second.size, i->second.file,
374                                i->second.line);
375                }
376        }
377}
378
379void memleakfinder_reportleaks()
380{
381        InternalAllocGuard guard;
382
383        // report to stdout
384        memleakfinder_reportleaks_file(stdout);
385}
386
387void memleakfinder_reportleaks_appendfile(const char *filename, const char *markertext)
388{
389        InternalAllocGuard guard;
390
391        FILE *file = ::fopen(filename, "a");
392        if(file != 0)
393        {
394                if(memleakfinder_numleaks() > 0)
395                {
396#ifdef HAVE_GETPID
397                        fprintf(file, "MEMORY LEAKS FROM PROCESS %d (%s)\n", getpid(), markertext);
398#else
399                        fprintf(file, "MEMORY LEAKS (%s)\n", markertext);
400#endif
401                        memleakfinder_reportleaks_file(file);
402                }
403       
404                ::fclose(file);
405        }
406        else
407        {
408                BOX_WARNING("Couldn't open memory leak results file " <<
409                        filename << " for appending");
410        }
411}
412
413static char atexit_filename[512];
414static char atexit_markertext[512];
415static bool atexit_registered = false;
416
417extern "C" void memleakfinder_atexit()
418{
419        memleakfinder_reportleaks_appendfile(atexit_filename, atexit_markertext);
420}
421
422void memleakfinder_setup_exit_report(const char *filename, const char *markertext)
423{
424        ::strncpy(atexit_filename, filename, sizeof(atexit_filename)-1);
425        ::strncpy(atexit_markertext, markertext, sizeof(atexit_markertext)-1);
426        atexit_filename[sizeof(atexit_filename)-1] = 0;
427        atexit_markertext[sizeof(atexit_markertext)-1] = 0;
428        if(!atexit_registered)
429        {
430                std::atexit(memleakfinder_atexit);
431                atexit_registered = true;
432        }
433}
434
435
436
437
438void add_object_block(void *block, size_t size, const char *file, int line, bool array)
439{
440        InternalAllocGuard guard;
441
442        if(!memleakfinder_global_enable) return;
443        if(!memleakfinder_initialised)   return;
444        ASSERT(!sTrackingDataDestroyed);
445
446        if(block != 0)
447        {
448                ObjectInfo i;
449                i.size = size;
450                i.file = file;
451                i.line = line;
452                i.array = array;
453                sObjectBlocks[block] = i;
454               
455                if(sTrackObjectsInSection)
456                {
457                        sSectionObjectBlocks[block] = i;
458                }
459        }
460}
461
462void remove_object_block(void *block)
463{
464        InternalAllocGuard guard;
465
466        if(!memleakfinder_global_enable) return;
467        if(!memleakfinder_initialised)   return;
468        if(sTrackingDataDestroyed)       return;
469
470        std::map<void *, ObjectInfo>::iterator i(sObjectBlocks.find(block));
471        if(i != sObjectBlocks.end())
472        {
473                sObjectBlocks.erase(i);
474        }
475
476        if(sTrackObjectsInSection)
477        {
478                std::map<void *, ObjectInfo>::iterator i(sSectionObjectBlocks.find(block));
479                if(i != sSectionObjectBlocks.end())
480                {
481                        sSectionObjectBlocks.erase(i);
482                }
483        }
484
485        // If it's not in the list, just ignore it, as lots of stuff goes this way...
486}
487
488static void *internal_new(size_t size, const char *file, int line)
489{
490        void *r;
491
492        {
493                InternalAllocGuard guard;
494                r = std::malloc(size);
495        }
496       
497        if (sInternalAllocDepth == 0)
498        {
499                InternalAllocGuard guard;
500                add_object_block(r, size, file, line, false);
501                //TRACE4("new(), %d, %s, %d, %08x\n", size, file, line, r);
502        }
503
504        return r;
505}
506
507void *operator new(size_t size, const char *file, int line)
508{
509        return internal_new(size, file, line);
510}
511
512void *operator new[](size_t size, const char *file, int line)
513{
514        return internal_new(size, file, line);
515}
516
517// where there is no doctor... need to override standard new() too
518// http://www.relisoft.com/book/tech/9new.html
519// disabled because it causes hangs on FC2 in futex() in test/common
520// while reading files. reason unknown.
521/*
522void *operator new(size_t size)
523{
524        return internal_new(size, "standard libraries", 0);
525}
526*/
527
528void *operator new[](size_t size)
529{
530        return internal_new(size, "standard libraries", 0);
531}
532
533void internal_delete(void *ptr)
534{
535        InternalAllocGuard guard;
536
537        std::free(ptr);
538        remove_object_block(ptr);
539        //TRACE1("delete[]() called, %08x\n", ptr);
540}
541
542void operator delete[](void *ptr) throw ()
543{
544        internal_delete(ptr);
545}
546
547void operator delete(void *ptr) throw ()
548{
549        internal_delete(ptr);
550}
551
552#endif // BOX_RELEASE_BUILD
Note: See TracBrowser for help on using the repository browser.