SUMO - Simulation of Urban MObility
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
debug_new.cpp
Go to the documentation of this file.
1 // -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 // vim:tabstop=4:shiftwidth=4:expandtab:
3 #ifdef _MSC_VER
4 #include <windows_config.h>
5 #else
6 #include <config.h>
7 #endif
8 #ifdef CHECK_MEMORY_LEAKS
9 /*
10  * Copyright (C) 2004-2008 Wu Yongwei <adah at users dot sourceforge dot net>
11  *
12  * This software is provided 'as-is', without any express or implied
13  * warranty. In no event will the authors be held liable for any
14  * damages arising from the use of this software.
15  *
16  * Permission is granted to anyone to use this software for any purpose,
17  * including commercial applications, and to alter it and redistribute
18  * it freely, subject to the following restrictions:
19  *
20  * 1. The origin of this software must not be misrepresented; you must
21  * not claim that you wrote the original software. If you use this
22  * software in a product, an acknowledgement in the product
23  * documentation would be appreciated but is not required.
24  * 2. Altered source versions must be plainly marked as such, and must
25  * not be misrepresented as being the original software.
26  * 3. This notice may not be removed or altered from any source
27  * distribution.
28  *
29  * This file is part of Stones of Nvwa:
30  * http://sourceforge.net/projects/nvwa
31  *
32  */
33 
44 #include <new>
45 #include <assert.h>
46 #include <limits.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #ifdef __unix__
51 #include <alloca.h>
52 #endif
53 #ifdef _WIN32
54 #include <malloc.h>
55 #endif
56 #include "fast_mutex.h"
57 #include "static_assert.h"
58 
59 #if !_FAST_MUTEX_CHECK_INITIALIZATION && !defined(_NOTHREADS)
60 #error "_FAST_MUTEX_CHECK_INITIALIZATION not set: check_leaks may not work"
61 #endif
62 
69 #ifndef _DEBUG_NEW_ALIGNMENT
70 #define _DEBUG_NEW_ALIGNMENT 16
71 #endif
72 
80 #ifndef _DEBUG_NEW_CALLER_ADDRESS
81 #ifdef __GNUC__
82 #define _DEBUG_NEW_CALLER_ADDRESS __builtin_return_address(0)
83 #else
84 #define _DEBUG_NEW_CALLER_ADDRESS NULL
85 #endif
86 #endif
87 
97 #ifndef _DEBUG_NEW_ERROR_ACTION
98 #ifndef _DEBUG_NEW_ERROR_CRASH
99 #define _DEBUG_NEW_ERROR_ACTION abort()
100 #else
101 #define _DEBUG_NEW_ERROR_ACTION do { *((char*)0) = 0; abort(); } while (0)
102 #endif
103 #endif
104 
116 #ifndef _DEBUG_NEW_FILENAME_LEN
117 #define _DEBUG_NEW_FILENAME_LEN 80
118 #endif
119 
132 #ifndef _DEBUG_NEW_PROGNAME
133 #define _DEBUG_NEW_PROGNAME NULL
134 #endif
135 
143 #ifndef _DEBUG_NEW_STD_OPER_NEW
144 #define _DEBUG_NEW_STD_OPER_NEW 1
145 #endif
146 
154 #ifndef _DEBUG_NEW_TAILCHECK
155 #define _DEBUG_NEW_TAILCHECK 0
156 #endif
157 
163 #ifndef _DEBUG_NEW_TAILCHECK_CHAR
164 #define _DEBUG_NEW_TAILCHECK_CHAR 0xCC
165 #endif
166 
175 #ifndef _DEBUG_NEW_USE_ADDR2LINE
176 #ifdef __GNUC__
177 #define _DEBUG_NEW_USE_ADDR2LINE 1
178 #else
179 #define _DEBUG_NEW_USE_ADDR2LINE 0
180 #endif
181 #endif
182 
183 #ifdef _MSC_VER
184 #pragma warning(disable: 4073) // #pragma init_seg(lib) used
185 #pragma warning(disable: 4290) // C++ exception specification ignored
186 #pragma init_seg(lib)
187 #endif
188 
189 #undef _DEBUG_NEW_EMULATE_MALLOC
190 #undef _DEBUG_NEW_REDEFINE_NEW
191 
195 #define _DEBUG_NEW_REDEFINE_NEW 0
196 #include "debug_new.h"
197 
201 #define align(s) \
202  (((s) + _DEBUG_NEW_ALIGNMENT - 1) & ~(_DEBUG_NEW_ALIGNMENT - 1))
203 
207 struct new_ptr_list_t
208 {
209  new_ptr_list_t* next;
210  new_ptr_list_t* prev;
211  size_t size;
212  union
213  {
214 #if _DEBUG_NEW_FILENAME_LEN == 0
215  const char* file;
216 #else
217  char file[_DEBUG_NEW_FILENAME_LEN];
218 #endif
219  void* addr;
220  };
221  unsigned line :31;
222  unsigned is_array :1;
223  unsigned magic;
224 };
225 
229 const unsigned MAGIC = 0x4442474E;
230 
234 const int ALIGNED_LIST_ITEM_SIZE = align(sizeof(new_ptr_list_t));
235 
239 static new_ptr_list_t new_ptr_list = {
240  &new_ptr_list,
241  &new_ptr_list,
242  0,
243  {
244 #if _DEBUG_NEW_FILENAME_LEN == 0
245  NULL
246 #else
247  ""
248 #endif
249  },
250  0,
251  0,
252  MAGIC
253 };
254 
258 static fast_mutex new_ptr_lock;
259 
263 static fast_mutex new_output_lock;
264 
268 static size_t total_mem_alloc = 0;
269 
274 bool new_autocheck_flag = true;
275 
279 bool new_verbose_flag = false;
280 
286 FILE* new_output_fp = stderr;
287 
296 const char* new_progname = _DEBUG_NEW_PROGNAME;
297 
298 #if _DEBUG_NEW_USE_ADDR2LINE
299 
308 static bool print_position_from_addr(const void* addr)
309 {
310  static const void* last_addr = NULL;
311  static char last_info[256] = "";
312  if (addr == last_addr)
313  {
314  if (last_info[0] == '\0')
315  return false;
316  fprintf(new_output_fp, "%s", last_info);
317  return true;
318  }
319  if (new_progname)
320  {
321  const char addr2line_cmd[] = "addr2line -e ";
322 #if defined(__CYGWIN__) || defined(_WIN32)
323  const int exeext_len = 4;
324 #else
325  const int exeext_len = 0;
326 #endif
327 #if !defined(__CYGWIN__) && defined(__unix__)
328  const char ignore_err[] = " 2>/dev/null";
329 #elif defined(__CYGWIN__) || \
330  (defined(_WIN32) && defined(WINVER) && WINVER >= 0x0500)
331  const char ignore_err[] = " 2>nul";
332 #else
333  const char ignore_err[] = "";
334 #endif
335  char* cmd = (char*)alloca(strlen(new_progname)
336  + exeext_len
337  + sizeof addr2line_cmd - 1
338  + sizeof ignore_err - 1
339  + sizeof(void*) * 2
340  + 4 /* SP + "0x" + null */);
341  strcpy(cmd, addr2line_cmd);
342  strcpy(cmd + sizeof addr2line_cmd - 1, new_progname);
343  size_t len = strlen(cmd);
344 #if defined(__CYGWIN__) || defined(_WIN32)
345  if (len <= 4
346  || (strcmp(cmd + len - 4, ".exe") != 0 &&
347  strcmp(cmd + len - 4, ".EXE") != 0))
348  {
349  strcpy(cmd + len, ".exe");
350  len += 4;
351  }
352 #endif
353  sprintf(cmd + len, " %p%s", addr, ignore_err);
354  FILE* fp = popen(cmd, "r");
355  if (fp)
356  {
357  char buffer[sizeof last_info] = "";
358  len = 0;
359  if (fgets(buffer, sizeof buffer, fp))
360  {
361  len = strlen(buffer);
362  if (buffer[len - 1] == '\n')
363  buffer[--len] = '\0';
364  }
365  int res = pclose(fp);
366  // Display the file/line information only if the command
367  // is executed successfully and the output points to a
368  // valid position, but the result will be cached if only
369  // the command is executed successfully.
370  if (res == 0 && len > 0)
371  {
372  last_addr = addr;
373  if (buffer[len - 1] == '0' && buffer[len - 2] == ':')
374  last_info[0] = '\0';
375  else
376  {
377  fprintf(new_output_fp, "%s", buffer);
378  strcpy(last_info, buffer);
379  return true;
380  }
381  }
382  }
383  }
384  return false;
385 }
386 #else
387 
393 static bool print_position_from_addr(const void*)
394 {
395  return false;
396 }
397 #endif // _DEBUG_NEW_USE_ADDR2LINE
398 
410 static void print_position(const void* ptr, int line)
411 {
412  if (line != 0) // Is file/line information present?
413  {
414  fprintf(new_output_fp, "%s:%d", (const char*)ptr, line);
415  }
416  else if (ptr != NULL) // Is caller address present?
417  {
418  if (!print_position_from_addr(ptr)) // Fail to get source position?
419  fprintf(new_output_fp, "%p", ptr);
420  }
421  else // No information is present
422  {
423  fprintf(new_output_fp, "<Unknown>");
424  }
425 }
426 
427 #if _DEBUG_NEW_TAILCHECK
428 
436 static bool check_tail(new_ptr_list_t* ptr)
437 {
438  const unsigned char* const pointer = (unsigned char*)ptr +
439  ALIGNED_LIST_ITEM_SIZE + ptr->size;
440  for (int i = 0; i < _DEBUG_NEW_TAILCHECK; ++i)
441  if (pointer[i] != _DEBUG_NEW_TAILCHECK_CHAR)
442  return false;
443  return true;
444 }
445 #endif
446 
457 static void* alloc_mem(size_t size, const char* file, int line, bool is_array)
458 {
459  assert(line >= 0);
460  STATIC_ASSERT((_DEBUG_NEW_ALIGNMENT & (_DEBUG_NEW_ALIGNMENT - 1)) == 0,
461  Alignment_must_be_power_of_two);
462  STATIC_ASSERT(_DEBUG_NEW_TAILCHECK >= 0, Invalid_tail_check_length);
463  size_t s = size + ALIGNED_LIST_ITEM_SIZE + _DEBUG_NEW_TAILCHECK;
464  new_ptr_list_t* ptr = (new_ptr_list_t*)malloc(s);
465  if (ptr == NULL)
466  {
467 #if _DEBUG_NEW_STD_OPER_NEW
468  return NULL;
469 #else
470  fast_mutex_autolock lock(new_output_lock);
471  fprintf(new_output_fp,
472  "Out of memory when allocating %u bytes\n",
473  size);
474  fflush(new_output_fp);
475  _DEBUG_NEW_ERROR_ACTION;
476 #endif
477  }
478  void* pointer = (char*)ptr + ALIGNED_LIST_ITEM_SIZE;
479 #if _DEBUG_NEW_FILENAME_LEN == 0
480  ptr->file = file;
481 #else
482  if (line)
483  strncpy(ptr->file, file, _DEBUG_NEW_FILENAME_LEN - 1)
484  [_DEBUG_NEW_FILENAME_LEN - 1] = '\0';
485  else
486  ptr->addr = (void*)file;
487 #endif
488  ptr->line = line;
489  ptr->is_array = is_array;
490  ptr->size = size;
491  ptr->magic = MAGIC;
492  {
493  fast_mutex_autolock lock(new_ptr_lock);
494  ptr->prev = new_ptr_list.prev;
495  ptr->next = &new_ptr_list;
496  new_ptr_list.prev->next = ptr;
497  new_ptr_list.prev = ptr;
498  }
499 #if _DEBUG_NEW_TAILCHECK
500  memset((char*)pointer + size, _DEBUG_NEW_TAILCHECK_CHAR,
501  _DEBUG_NEW_TAILCHECK);
502 #endif
503  if (new_verbose_flag)
504  {
505  fast_mutex_autolock lock(new_output_lock);
506  fprintf(new_output_fp,
507  "new%s: allocated %p (size %lu, ",
508  is_array ? "[]" : "",
509  pointer, size);
510  if (line != 0)
511  print_position(ptr->file, ptr->line);
512  else
513  print_position(ptr->addr, ptr->line);
514  fprintf(new_output_fp, ")\n");
515  }
516  total_mem_alloc += size;
517  return pointer;
518 }
519 
528 static void free_pointer(void* pointer, void* addr, bool is_array)
529 {
530  if (pointer == NULL)
531  return;
532  new_ptr_list_t* ptr =
533  (new_ptr_list_t*)((char*)pointer - ALIGNED_LIST_ITEM_SIZE);
534  if (ptr->magic != MAGIC)
535  {
536  {
537  fast_mutex_autolock lock(new_output_lock);
538  fprintf(new_output_fp, "delete%s: invalid pointer %p (",
539  is_array ? "[]" : "", pointer);
540  print_position(addr, 0);
541  fprintf(new_output_fp, ")\n");
542  }
543  check_mem_corruption();
544  fflush(new_output_fp);
545  _DEBUG_NEW_ERROR_ACTION;
546  }
547  if (is_array != ptr->is_array)
548  {
549  const char* msg;
550  if (is_array)
551  msg = "delete[] after new";
552  else
553  msg = "delete after new[]";
554  fast_mutex_autolock lock(new_output_lock);
555  fprintf(new_output_fp,
556  "%s: pointer %p (size %lu)\n\tat ",
557  msg,
558  (char*)ptr + ALIGNED_LIST_ITEM_SIZE,
559  ptr->size);
560  print_position(addr, 0);
561  fprintf(new_output_fp, "\n\toriginally allocated at ");
562  if (ptr->line != 0)
563  print_position(ptr->file, ptr->line);
564  else
565  print_position(ptr->addr, ptr->line);
566  fprintf(new_output_fp, "\n");
567  fflush(new_output_fp);
568  _DEBUG_NEW_ERROR_ACTION;
569  }
570 #if _DEBUG_NEW_TAILCHECK
571  if (!check_tail(ptr))
572  {
573  check_mem_corruption();
574  fflush(new_output_fp);
575  _DEBUG_NEW_ERROR_ACTION;
576  }
577 #endif
578  {
579  fast_mutex_autolock lock(new_ptr_lock);
580  total_mem_alloc -= ptr->size;
581  ptr->magic = 0;
582  ptr->prev->next = ptr->next;
583  ptr->next->prev = ptr->prev;
584  }
585  if (new_verbose_flag)
586  {
587  fast_mutex_autolock lock(new_output_lock);
588  fprintf(new_output_fp,
589  "delete%s: freed %p (size %lu, %lu bytes still allocated)\n",
590  is_array ? "[]" : "",
591  (char*)ptr + ALIGNED_LIST_ITEM_SIZE,
592  ptr->size, total_mem_alloc);
593  }
594  free(ptr);
595  return;
596 }
597 
603 int check_leaks()
604 {
605  int leak_cnt = 0;
606  fast_mutex_autolock lock_ptr(new_ptr_lock);
607  fast_mutex_autolock lock_output(new_output_lock);
608  new_ptr_list_t* ptr = new_ptr_list.next;
609  while (ptr != &new_ptr_list)
610  {
611  const char* const pointer = (char*)ptr + ALIGNED_LIST_ITEM_SIZE;
612  if (ptr->magic != MAGIC)
613  {
614  fprintf(new_output_fp,
615  "warning: heap data corrupt near %p\n",
616  pointer);
617  }
618 #if _DEBUG_NEW_TAILCHECK
619  if (!check_tail(ptr))
620  {
621  fprintf(new_output_fp,
622  "warning: overwritten past end of object at %p\n",
623  pointer);
624  }
625 #endif
626  fprintf(new_output_fp,
627  "Leaked object at %p (size %lu, ",
628  pointer,
629  ptr->size);
630  if (ptr->line != 0)
631  print_position(ptr->file, ptr->line);
632  else
633  print_position(ptr->addr, ptr->line);
634  fprintf(new_output_fp, ")\n");
635  ptr = ptr->next;
636  ++leak_cnt;
637  }
638  if (new_verbose_flag || leak_cnt)
639  fprintf(new_output_fp, "*** %d leaks found\n", leak_cnt);
640  return leak_cnt;
641 }
642 
649 int check_mem_corruption()
650 {
651  int corrupt_cnt = 0;
652  fast_mutex_autolock lock_ptr(new_ptr_lock);
653  fast_mutex_autolock lock_output(new_output_lock);
654  fprintf(new_output_fp, "*** Checking for memory corruption: START\n");
655  for (new_ptr_list_t* ptr = new_ptr_list.next;
656  ptr != &new_ptr_list;
657  ptr = ptr->next)
658  {
659  const char* const pointer = (char*)ptr + ALIGNED_LIST_ITEM_SIZE;
660  if (ptr->magic == MAGIC
661 #if _DEBUG_NEW_TAILCHECK
662  && check_tail(ptr)
663 #endif
664  )
665  continue;
666 #if _DEBUG_NEW_TAILCHECK
667  if (ptr->magic != MAGIC)
668  {
669 #endif
670  fprintf(new_output_fp,
671  "Heap data corrupt near %p (size %lu, ",
672  pointer,
673  ptr->size);
674 #if _DEBUG_NEW_TAILCHECK
675  }
676  else
677  {
678  fprintf(new_output_fp,
679  "Overwritten past end of object at %p (size %u, ",
680  pointer,
681  ptr->size);
682  }
683 #endif
684  if (ptr->line != 0)
685  print_position(ptr->file, ptr->line);
686  else
687  print_position(ptr->addr, ptr->line);
688  fprintf(new_output_fp, ")\n");
689  ++corrupt_cnt;
690  }
691  fprintf(new_output_fp, "*** Checking for memory corruption: %d FOUND\n",
692  corrupt_cnt);
693  return corrupt_cnt;
694 }
695 
696 void __debug_new_recorder::_M_process(void* pointer)
697 {
698  if (pointer == NULL)
699  return;
700  new_ptr_list_t* ptr =
701  (new_ptr_list_t*)((char*)pointer - ALIGNED_LIST_ITEM_SIZE);
702  if (ptr->magic != MAGIC || ptr->line != 0)
703  {
704  fast_mutex_autolock lock(new_output_lock);
705  fprintf(new_output_fp,
706  "warning: debug_new used with placement new (%s:%d)\n",
707  _M_file, _M_line);
708  return;
709  }
710 #if _DEBUG_NEW_FILENAME_LEN == 0
711  ptr->file = _M_file;
712 #else
713  strncpy(ptr->file, _M_file, _DEBUG_NEW_FILENAME_LEN - 1)
714  [_DEBUG_NEW_FILENAME_LEN - 1] = '\0';
715 #endif
716  ptr->line = _M_line;
717 }
718 
719 void* operator new(size_t size, const char* file, int line)
720 {
721  void* ptr = alloc_mem(size, file, line, false);
722 #if _DEBUG_NEW_STD_OPER_NEW
723  if (ptr)
724  return ptr;
725  else
726  throw std::bad_alloc();
727 #else
728  return ptr;
729 #endif
730 }
731 
732 void* operator new[](size_t size, const char* file, int line)
733 {
734  void* ptr = alloc_mem(size, file, line, true);
735 #if _DEBUG_NEW_STD_OPER_NEW
736  if (ptr)
737  return ptr;
738  else
739  throw std::bad_alloc();
740 #else
741  return ptr;
742 #endif
743 }
744 
745 void* operator new(size_t size) throw(std::bad_alloc)
746 {
747  return operator new(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
748 }
749 
750 void* operator new[](size_t size) throw(std::bad_alloc)
751 {
752  return operator new[](size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
753 }
754 
755 #if !defined(__BORLANDC__) || __BORLANDC__ > 0x551
756 void* operator new(size_t size, const std::nothrow_t&) throw()
757 {
758  return alloc_mem(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0, false);
759 }
760 
761 void* operator new[](size_t size, const std::nothrow_t&) throw()
762 {
763  return alloc_mem(size, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0, true);
764 }
765 #endif
766 
767 void operator delete(void* pointer) throw()
768 {
769  free_pointer(pointer, _DEBUG_NEW_CALLER_ADDRESS, false);
770 }
771 
772 void operator delete[](void* pointer) throw()
773 {
774  free_pointer(pointer, _DEBUG_NEW_CALLER_ADDRESS, true);
775 }
776 
777 #if HAVE_PLACEMENT_DELETE
778 void operator delete(void* pointer, const char* file, int line) throw()
779 {
780  if (new_verbose_flag)
781  {
782  fast_mutex_autolock lock(new_output_lock);
783  fprintf(new_output_fp,
784  "info: exception thrown on initializing object at %p (",
785  pointer);
786  print_position(file, line);
787  fprintf(new_output_fp, ")\n");
788  }
789  operator delete(pointer);
790 }
791 
792 void operator delete[](void* pointer, const char* file, int line) throw()
793 {
794  if (new_verbose_flag)
795  {
796  fast_mutex_autolock lock(new_output_lock);
797  fprintf(new_output_fp,
798  "info: exception thrown on initializing objects at %p (",
799  pointer);
800  print_position(file, line);
801  fprintf(new_output_fp, ")\n");
802  }
803  operator delete[](pointer);
804 }
805 
806 void operator delete(void* pointer, const std::nothrow_t&) throw()
807 {
808  operator delete(pointer, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
809 }
810 
811 void operator delete[](void* pointer, const std::nothrow_t&) throw()
812 {
813  operator delete[](pointer, (char*)_DEBUG_NEW_CALLER_ADDRESS, 0);
814 }
815 #endif // HAVE_PLACEMENT_DELETE
816 
817 int __debug_new_counter::_S_count = 0;
818 
822 __debug_new_counter::__debug_new_counter()
823 {
824  ++_S_count;
825 }
826 
831 __debug_new_counter::~__debug_new_counter()
832 {
833  if (--_S_count == 0 && new_autocheck_flag)
834  if (check_leaks())
835  {
836  new_verbose_flag = true;
837 #if defined(__GNUC__) && __GNUC__ >= 3
838  if (!getenv("GLIBCPP_FORCE_NEW") && !getenv("GLIBCXX_FORCE_NEW"))
839  fprintf(new_output_fp,
840 "*** WARNING: GCC 3 or later is detected, please make sure the\n"
841 " environment variable GLIBCPP_FORCE_NEW (GCC 3.2 and 3.3) or\n"
842 " GLIBCXX_FORCE_NEW (GCC 3.4 and later) is defined. Check the\n"
843 " README file for details.\n");
844 #endif
845  }
846 }
847 
848 
849 #endif