debug_new.cpp

Go to the documentation of this file.
00001 // -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
00002 // vim:tabstop=4:shiftwidth=4:expandtab:
00003 
00004 /*
00005  * Copyright (C) 2004-2005 Wu Yongwei <adah at users dot sourceforge dot net>
00006  *
00007  * This software is provided 'as-is', without any express or implied
00008  * warranty.  In no event will the authors be held liable for any
00009  * damages arising from the use of this software.
00010  *
00011  * Permission is granted to anyone to use this software for any purpose,
00012  * including commercial applications, and to alter it and redistribute
00013  * it freely, subject to the following restrictions:
00014  *
00015  * 1. The origin of this software must not be misrepresented; you must
00016  *    not claim that you wrote the original software. If you use this
00017  *    software in a product, an acknowledgment in the product
00018  *    documentation would be appreciated but is not required.
00019  * 2. Altered source versions must be plainly marked as such, and must
00020  *    not be misrepresented as being the original software.
00021  * 3. This notice may not be removed or altered from any source
00022  *    distribution.
00023  *
00024  * This file is part of Stones of Nvwa:
00025  *      http://sourceforge.net/projects/nvwa
00026  *
00027  */
00028 
00039 #ifdef _MSC_VER
00040 #include <windows_config.h>
00041 #else
00042 #include <config.h>
00043 #endif
00044 
00045 #ifdef CHECK_MEMORY_LEAKS
00046 
00047 #include <new>
00048 #include <assert.h>
00049 #include <limits.h>
00050 #include <stdio.h>
00051 #include <stdlib.h>
00052 #include <string.h>
00053 #ifdef __unix__
00054 #include <alloca.h>
00055 #endif
00056 #ifdef _WIN32
00057 #include <malloc.h>
00058 #endif
00059 #include "fast_mutex.h"
00060 #include "static_assert.h"
00061 
00062 #if !_FAST_MUTEX_CHECK_INITIALIZATION && !defined(_NOTHREADS)
00063 #error "_FAST_MUTEX_CHECK_INITIALIZATION not set: check_leaks may not work"
00064 #endif
00065 
00072 #ifndef _DEBUG_NEW_ALIGNMENT
00073 #define _DEBUG_NEW_ALIGNMENT 16
00074 #endif
00075 
00083 #ifndef _DEBUG_NEW_CALLER_ADDRESS
00084 #ifdef __GNUC__
00085 #define _DEBUG_NEW_CALLER_ADDRESS __builtin_return_address(0)
00086 #else
00087 #define _DEBUG_NEW_CALLER_ADDRESS NULL
00088 #endif
00089 #endif
00090 
00100 #ifndef _DEBUG_NEW_ERROR_ACTION
00101 #ifndef _DEBUG_NEW_ERROR_CRASH
00102 #define _DEBUG_NEW_ERROR_ACTION abort()
00103 #else
00104 #define _DEBUG_NEW_ERROR_ACTION do { *((char*)0) = 0; abort(); } while (0)
00105 #endif
00106 #endif
00107 
00120 #ifndef _DEBUG_NEW_FILENAME_LEN
00121 #define _DEBUG_NEW_FILENAME_LEN 80
00122 #endif
00123 
00131 #ifndef _DEBUG_NEW_HASHTABLESIZE
00132 #define _DEBUG_NEW_HASHTABLESIZE 16384
00133 #endif
00134 
00141 #ifndef _DEBUG_NEW_HASH
00142 #define _DEBUG_NEW_HASH(p) (((size_t)(p) >> 8) % _DEBUG_NEW_HASHTABLESIZE)
00143 #endif
00144 
00157 #ifndef _DEBUG_NEW_PROGNAME
00158 #define _DEBUG_NEW_PROGNAME NULL
00159 #endif
00160 
00169 #ifndef _DEBUG_NEW_USE_ADDR2LINE
00170 #ifdef __GNUC__
00171 #define _DEBUG_NEW_USE_ADDR2LINE 1
00172 #else
00173 #define _DEBUG_NEW_USE_ADDR2LINE 0
00174 #endif
00175 #endif
00176 
00177 #ifdef _MSC_VER
00178 #pragma warning(disable: 4073)  // #pragma init_seg(lib) used
00179 #pragma warning(disable: 4290)  // C++ exception specification ignored
00180 #pragma init_seg(lib)
00181 #endif
00182 
00183 #undef  _DEBUG_NEW_EMULATE_MALLOC
00184 #undef  _DEBUG_NEW_REDEFINE_NEW
00185 
00189 #define _DEBUG_NEW_REDEFINE_NEW 0
00190 #include "debug_new.h"
00191 
00195 #define align(s) \
00196         (((s) + _DEBUG_NEW_ALIGNMENT - 1) & ~(_DEBUG_NEW_ALIGNMENT - 1))
00197 
00201 struct new_ptr_list_t
00202 {
00203     new_ptr_list_t*     next;
00204     union
00205     {
00206 #if _DEBUG_NEW_FILENAME_LEN == 0
00207     const char*         file;
00208 #else
00209     char                file[_DEBUG_NEW_FILENAME_LEN];
00210 #endif
00211     void*               addr;
00212     };
00213     int                 line;
00214     size_t              size;
00215 };
00216 
00220 const int aligned_list_item_size = align(sizeof(new_ptr_list_t));
00221 
00225 static new_ptr_list_t* new_ptr_list[_DEBUG_NEW_HASHTABLESIZE];
00226 
00231 static fast_mutex new_ptr_lock[_DEBUG_NEW_HASHTABLESIZE];
00232 
00236 static fast_mutex new_output_lock;
00237 
00241 static size_t total_mem_alloc = 0;
00242 
00247 bool new_autocheck_flag = true;
00248 
00252 bool new_verbose_flag = false;
00253 
00259 FILE* new_output_fp = stderr;
00260 
00269 const char* new_progname = _DEBUG_NEW_PROGNAME;
00270 
00271 #if _DEBUG_NEW_USE_ADDR2LINE
00272 
00281 static bool print_position_from_addr(const void* addr)
00282 {
00283     static const void* last_addr = NULL;
00284     static char last_info[256] = "";
00285     if (addr == last_addr)
00286     {
00287         if (last_info[0] == '\0')
00288             return false;
00289         fprintf(new_output_fp, "%s", last_info);
00290         return true;
00291     }
00292     if (new_progname)
00293     {
00294         const char addr2line_cmd[] = "addr2line -e ";
00295 #if   defined(__CYGWIN__) || defined(_WIN32)
00296         const int  exeext_len = 4;
00297 #else
00298         const int  exeext_len = 0;
00299 #endif
00300 #if  !defined(__CYGWIN__) && defined(__unix__)
00301         const char ignore_err[] = " 2>/dev/null";
00302 #elif defined(__CYGWIN__) || \
00303         (defined(_WIN32) && defined(WINVER) && WINVER >= 0x0500)
00304         const char ignore_err[] = " 2>nul";
00305 #else
00306         const char ignore_err[] = "";
00307 #endif
00308         char* cmd = (char*)alloca(strlen(new_progname)
00309                                   + exeext_len
00310                                   + sizeof addr2line_cmd - 1
00311                                   + sizeof ignore_err - 1
00312                                   + sizeof(void*) * 2
00313                                   + 4 /* SP + "0x" + null */);
00314         strcpy(cmd, addr2line_cmd);
00315         strcpy(cmd + sizeof addr2line_cmd - 1, new_progname);
00316         size_t len = strlen(cmd);
00317 #if   defined(__CYGWIN__) || defined(_WIN32)
00318         if (len <= 4
00319                 || (strcmp(cmd + len - 4, ".exe") != 0 &&
00320                     strcmp(cmd + len - 4, ".EXE") != 0))
00321         {
00322             strcpy(cmd + len, ".exe");
00323             len += 4;
00324         }
00325 #endif
00326         sprintf(cmd + len, " %p%s", addr, ignore_err);
00327         FILE* fp = popen(cmd, "r");
00328         if (fp)
00329         {
00330             char buffer[sizeof last_info] = "";
00331             len = 0;
00332             if (fgets(buffer, sizeof buffer, fp))
00333             {
00334                 len = strlen(buffer);
00335                 if (buffer[len - 1] == '\n')
00336                     buffer[--len] = '\0';
00337             }
00338             int res = pclose(fp);
00339             // Display the file/line information only if the command
00340             // is executed successfully and the output points to a
00341             // valid position, but the result will be cached if only
00342             // the command is executed successfully.
00343             if (res == 0 && len > 0)
00344             {
00345                 last_addr = addr;
00346                 if (buffer[len - 1] == '0' && buffer[len - 2] == ':')
00347                     last_info[0] = '\0';
00348                 else
00349                 {
00350                     fprintf(new_output_fp, "%s", buffer);
00351                     strcpy(last_info, buffer);
00352                     return true;
00353                 }
00354             }
00355         }
00356     }
00357     return false;
00358 }
00359 #else
00360 
00366 static bool print_position_from_addr(const void*)
00367 {
00368     return false;
00369 }
00370 #endif // _DEBUG_NEW_USE_ADDR2LINE
00371 
00383 static void print_position(const void* ptr, int line)
00384 {
00385     line &= ~INT_MIN;       // Result from new[] if highest bit set: Ignore
00386     if (line != 0)          // Is file/line information present?
00387     {
00388         fprintf(new_output_fp, "%s:%d", (const char*)ptr, line);
00389     }
00390     else if (ptr != NULL)   // Is caller address present?
00391     {
00392         if (!print_position_from_addr(ptr)) // Fail to get source position?
00393             fprintf(new_output_fp, "%p", ptr);
00394     }
00395     else                    // No information is present
00396     {
00397         fprintf(new_output_fp, "<Unknown>");
00398     }
00399 }
00400 
00411 static new_ptr_list_t** search_pointer(void* pointer, size_t hash_index)
00412 {
00413     new_ptr_list_t** raw_ptr = &new_ptr_list[hash_index];
00414     while (*raw_ptr)
00415     {
00416         if ((char*)*raw_ptr + aligned_list_item_size == pointer)
00417         {
00418             return raw_ptr;
00419         }
00420         raw_ptr = &(*raw_ptr)->next;
00421     }
00422     return NULL;
00423 }
00424 
00436 static void free_pointer(new_ptr_list_t** raw_ptr, void* addr, bool array_mode)
00437 {
00438     new_ptr_list_t* ptr = *raw_ptr;
00439     int array_mode_mismatch = array_mode ^ ((ptr->line & INT_MIN) != 0);
00440     if (array_mode_mismatch)
00441     {
00442         const char* msg;
00443         if (array_mode)
00444             msg = "delete[] after new";
00445         else
00446             msg = "delete after new[]";
00447         fast_mutex_autolock lock(new_output_lock);
00448         fprintf(new_output_fp,
00449                 "%s: pointer %p (size %u)\n\tat ",
00450                 msg,
00451                 (char*)ptr + aligned_list_item_size,
00452                 ptr->size);
00453         print_position(addr, 0);
00454         fprintf(new_output_fp, "\n\toriginally allocated at ");
00455         if ((ptr->line & ~INT_MIN) != 0)
00456             print_position(ptr->file, ptr->line);
00457         else
00458             print_position(ptr->addr, ptr->line);
00459         fprintf(new_output_fp, "\n");
00460         fflush(new_output_fp);
00461         _DEBUG_NEW_ERROR_ACTION;
00462     }
00463     total_mem_alloc -= ptr->size;
00464     if (new_verbose_flag)
00465     {
00466         fast_mutex_autolock lock(new_output_lock);
00467         fprintf(new_output_fp,
00468                 "delete: freeing  %p (size %u, %u bytes still allocated)\n",
00469                 (char*)ptr + aligned_list_item_size,
00470                 ptr->size, total_mem_alloc);
00471     }
00472     *raw_ptr = ptr->next;
00473     free(ptr);
00474     return;
00475 }
00476 
00482 int check_leaks()
00483 {
00484     int leak_cnt = 0;
00485     for (int i = 0; i < _DEBUG_NEW_HASHTABLESIZE; ++i)
00486     {
00487         fast_mutex_autolock lock(new_ptr_lock[i]);
00488         new_ptr_list_t* ptr = new_ptr_list[i];
00489         if (ptr == NULL)
00490             continue;
00491         while (ptr)
00492         {
00493             fast_mutex_autolock lock(new_output_lock);
00494             fprintf(new_output_fp,
00495                     "Leaked object at %p (size %u, ",
00496                     (char*)ptr + aligned_list_item_size,
00497                     ptr->size);
00498             if ((ptr->line & ~INT_MIN) != 0)
00499                 print_position(ptr->file, ptr->line);
00500             else
00501                 print_position(ptr->addr, ptr->line);
00502             fprintf(new_output_fp, ")\n");
00503             ptr = ptr->next;
00504             ++leak_cnt;
00505         }
00506     }
00507     return leak_cnt;
00508 }
00509 
00510 void* operator new(size_t size, const char* file, int line)
00511 {
00512     assert((line & INT_MIN) == 0);
00513     STATIC_ASSERT((_DEBUG_NEW_ALIGNMENT & (_DEBUG_NEW_ALIGNMENT - 1)) == 0,
00514                   Alignment_must_be_power_of_two);
00515     size_t s = size + aligned_list_item_size;
00516     new_ptr_list_t* ptr = (new_ptr_list_t*)malloc(s);
00517     if (ptr == NULL)
00518     {
00519         fast_mutex_autolock lock(new_output_lock);
00520         fprintf(new_output_fp,
00521                 "new:  out of memory when allocating %u bytes\n",
00522                 size);
00523         fflush(new_output_fp);
00524         _DEBUG_NEW_ERROR_ACTION;
00525     }
00526     void* pointer = (char*)ptr + aligned_list_item_size;
00527     size_t hash_index = _DEBUG_NEW_HASH(pointer);
00528 #if _DEBUG_NEW_FILENAME_LEN == 0
00529     ptr->file = file;
00530 #else
00531     if (line)
00532         strncpy(ptr->file, file, _DEBUG_NEW_FILENAME_LEN - 1)
00533                 [_DEBUG_NEW_FILENAME_LEN - 1] = '\0';
00534     else
00535         ptr->addr = (void*)file;
00536 #endif
00537     ptr->line = line;
00538     ptr->size = size;
00539     {
00540         fast_mutex_autolock lock(new_ptr_lock[hash_index]);
00541         ptr->next = new_ptr_list[hash_index];
00542         new_ptr_list[hash_index] = ptr;
00543     }
00544     if (new_verbose_flag)
00545     {
00546         fast_mutex_autolock lock(new_output_lock);
00547         fprintf(new_output_fp,
00548                 "new:  allocated  %p (size %u, ",
00549                 pointer, size);
00550         if (line != 0)
00551             print_position(ptr->file, ptr->line);
00552         else
00553             print_position(ptr->addr, ptr->line);
00554         fprintf(new_output_fp, ")\n");
00555     }
00556     total_mem_alloc += size;
00557     return pointer;
00558 }
00559 
00560 void* operator new[](size_t size, const char* file, int line)
00561 {
00562     void* pointer = operator new(size, file, line);
00563     new_ptr_list_t* ptr =
00564             (new_ptr_list_t*)((char*)pointer - aligned_list_item_size);
00565     assert((ptr->line & INT_MIN) == 0);
00566     ptr->line |= INT_MIN;   // Result from new[] if highest bit set: Set
00567     return pointer;
00568 }
00569 
00570 void* operator new(size_t size) throw(std::bad_alloc)
00571 {
00572     return operator new(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
00573 }
00574 
00575 void* operator new[](size_t size) throw(std::bad_alloc)
00576 {
00577     return operator new[](size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
00578 }
00579 
00580 #if !defined(__BORLANDC__) || __BORLANDC__ > 0x551
00581 void* operator new(size_t size, const std::nothrow_t&) throw()
00582 {
00583     return operator new(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
00584 }
00585 
00586 void* operator new[](size_t size, const std::nothrow_t&) throw()
00587 {
00588     return operator new[](size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
00589 }
00590 #endif
00591 
00592 void operator delete(void* pointer) throw()
00593 {
00594     if (pointer == NULL)
00595         return;
00596     size_t hash_index = _DEBUG_NEW_HASH(pointer);
00597     fast_mutex_autolock lock(new_ptr_lock[hash_index]);
00598     new_ptr_list_t** raw_ptr = search_pointer(pointer, hash_index);
00599     if (raw_ptr == NULL)
00600     {
00601         fast_mutex_autolock lock(new_output_lock);
00602         fprintf(new_output_fp, "delete: invalid pointer %p at ", pointer);
00603         print_position(_DEBUG_NEW_CALLER_ADDRESS, 0);
00604         fprintf(new_output_fp, "\n");
00605         fflush(new_output_fp);
00606         _DEBUG_NEW_ERROR_ACTION;
00607     }
00608     free_pointer(raw_ptr, _DEBUG_NEW_CALLER_ADDRESS, false);
00609 }
00610 
00611 void operator delete[](void* pointer) throw()
00612 {
00613     if (pointer == NULL)
00614         return;
00615     size_t hash_index = _DEBUG_NEW_HASH(pointer);
00616     fast_mutex_autolock lock(new_ptr_lock[hash_index]);
00617     new_ptr_list_t** raw_ptr = search_pointer(pointer, hash_index);
00618     if (raw_ptr == NULL)
00619     {
00620         fast_mutex_autolock lock(new_output_lock);
00621         fprintf(new_output_fp, "delete[]: invalid pointer %p at ", pointer);
00622         print_position(_DEBUG_NEW_CALLER_ADDRESS, 0);
00623         fprintf(new_output_fp, "\n");
00624         fflush(new_output_fp);
00625         _DEBUG_NEW_ERROR_ACTION;
00626     }
00627     free_pointer(raw_ptr, _DEBUG_NEW_CALLER_ADDRESS, true);
00628 }
00629 
00630 #if HAS_PLACEMENT_DELETE
00631 void operator delete(void* pointer, const char* file, int line) throw()
00632 {
00633     if (new_verbose_flag)
00634     {
00635         fast_mutex_autolock lock(new_output_lock);
00636         fprintf(new_output_fp,
00637                 "info: exception thrown on initializing object at %p (",
00638                 pointer);
00639         print_position(file, line);
00640         fprintf(new_output_fp, ")\n");
00641     }
00642     operator delete(pointer);
00643 }
00644 
00645 void operator delete[](void* pointer, const char* file, int line) throw()
00646 {
00647     if (new_verbose_flag)
00648     {
00649         fast_mutex_autolock lock(new_output_lock);
00650         fprintf(new_output_fp,
00651                 "info: exception thrown on initializing objects at %p (",
00652                 pointer);
00653         print_position(file, line);
00654         fprintf(new_output_fp, ")\n");
00655     }
00656     operator delete[](pointer);
00657 }
00658 
00659 void operator delete(void* pointer, const std::nothrow_t&) throw()
00660 {
00661     operator delete(pointer, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
00662 }
00663 
00664 void operator delete[](void* pointer, const std::nothrow_t&) throw()
00665 {
00666     operator delete[](pointer, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
00667 }
00668 #endif // HAS_PLACEMENT_DELETE
00669 
00670 int __debug_new_counter::_count = 0;
00671 
00675 __debug_new_counter::__debug_new_counter()
00676 {
00677     ++_count;
00678 }
00679 
00684 __debug_new_counter::~__debug_new_counter()
00685 {
00686     if (--_count == 0 && new_autocheck_flag)
00687         if (check_leaks())
00688         {
00689             new_verbose_flag = true;
00690 #if defined(__GNUC__) && __GNUC__ >= 3
00691             if (!getenv("GLIBCPP_FORCE_NEW") && !getenv("GLIBCXX_FORCE_NEW"))
00692                 fprintf(new_output_fp,
00693 "*** WARNING:  GCC 3 or later is detected, please make sure the\n"
00694 "    environment variable GLIBCPP_FORCE_NEW (GCC 3.2 and 3.3) or\n"
00695 "    GLIBCXX_FORCE_NEW (GCC 3.4 and later) is defined.  Check the\n"
00696 "    README file for details.\n");
00697 #endif
00698         }
00699 }
00700 
00701 #endif // CHECK_MEMORY_LEAKS

Generated on Wed May 5 00:06:28 2010 for Sumo - Simulation of Urban MObility by  doxygen 1.5.6