-
Notifications
You must be signed in to change notification settings - Fork 4
/
debugmalloc.cc
5122 lines (4812 loc) · 190 KB
/
debugmalloc.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// $Header$
//
// \file debugmalloc.cc
// This file contains the memory allocations related debugging code.
//
// Copyright (C) 2000 - 2004, by
//
// Carlo Wood, Run on IRC <carlo@alinoe.com>
// RSA-1024 0x624ACAD5 1997-01-26 Sign & Encrypt
// Fingerprint16 = 32 EC A7 B6 AC DB 65 A6 F6 F6 55 DD 1C DC FF 61
//
// This file may be distributed under the terms of the Q Public License
// version 1.0 as appearing in the file LICENSE.QPL included in the
// packaging of this file.
//
//=============================================================================
//
// This file can be viewed at as an object, with __libcwd_tsd containing its
// private attributes. The public 'manipulator' interface are the memory
// allocation functions:
//
// - malloc, calloc, realloc free (and register_external_allocation if
// the macro kludge is being used (if we don't define these functions
// with external linkage)).
// - new and delete.
//
// These functions maintain an internal data structure that allows for
// - checking if calls to free and realloc have a valid pointer.
// - finding allocated-memory statistics.
// - finding the memory block given an arbitrary pointer that points inside it.
//
// We maintain two main data 'trees' per thread:
// 1) A red/black tree (using STL's map class) which contains all
// allocated memory blocks, except:
// - those allocated in this file while `internal' is true.
// - those allocated after a call to set_alloc_checking_off()
// (and before the corresponding set_alloc_checking_on()).
// 2) A tree of objects that allows us to see which object allocated what.
//
// The first tree is used for two main things:
// - Checking whether or not a call to free(3) or realloc(3) contains a valid
// pointer, and
// - Allowing us to quickly find back which allocated block belongs to any
// arbitrary pointer (or interval if needed).
// This allows us for example to find back the start of an instance of an
// object, inside a method of a base class of this object.
// This tree also contains type of allocation (new/new[]/malloc/realloc/valloc/memalign/posix_memalign/aligned_alloc).
//
// The second tree, existing of linked dm_alloc_ct objects, stores the data related
// to the memory block itself, currently its start, size and - when provided by the
// user by means of AllocTag() - a type_info_ct of the returned pointer and a description.
// The second tree allows us to group allocations in a hierarchy: It's needed
// for the function that shows the current allocations, or the allocations
// made and/or left over since a call to some function or since a 'marker'
// was set. It is also needed for memory leak detection (which just means
// that you set a marker somewhere and expect no extra allocated memory
// performed by the current thread at the point where you delete it).
//
// The memory management of these objects is as follows:
//
// new, new[], malloc and calloc create a memblk_key_ct and memblk_info_ct which
// are stored as a std::pair<memblk_key_ct const, memblk_info_ct> in the STL map.
//
// Creating the memblk_info_ct creates a related dm_alloc_ct.
//
// delete and free erase the std::pair<memblk_key_ct const, memblk_info_ct> from
// the map and as such destroy the memblk_key_ct and the memblk_info_ct.
//
// The memblk_info_ct has a lockable_auto_ptr to the allocated dm_alloc_ct: when
// the memblk_info_ct is destructed, so is the lockable_auto_ptr. When the
// lockable_auto_ptr still is the owner of the dm_alloc_ct object, then the
// dm_alloc_ct is destructed too.
//
// Creation and destruction of the dm_alloc_ct takes care of its own insertion
// and deletion in the hierarchy automatically, including updating of total
// allocated memory size, number of allocated blocks and the current list that
// newly allocated blocks must be added to.
//
// Two corresponding memblk_info_ct and dm_alloc_ct objects can be 'decoupled'
// in two ways:
// - The memblk_info_ct is erased but the corresponding dm_alloc_ct still has
// a list of its own. In which case the dm_alloc_ct is deleted as soon as its
// last list element is removed.
// - The memory block is made 'invisible' (with `make_invisible') which means
// that the dm_alloc_ct is deleted without deleting the corresponding
// memblk_info_ct.
//
// The current 'accessor' functions are:
//
// - alloc_ct const* find_alloc(void const* ptr);
// Find `alloc_ct' of memory block that `ptr' points to, or
// points inside of. Returns NULL when no block was found.
// - bool test_delete(void const* ptr)
// Returns true if `ptr' points to the start of an allocated
// memory block.
// - size_t mem_size()
// Returns the total ammount of allocated memory in bytes
// (the sum all blocks shown by `list_allocations_on').
// - unsigned long mem_blocks()
// Returns the total number of allocated memory blocks
// (Those that are shown by `list_allocations_on').
// - std::ostream& operator<<(std::ostream& o, debugmalloc_report_ct)
// Allows to write a memory allocation report to std::ostream `o'.
// - unsigned long list_allocations_on(debug_ct& debug_object, alloc_filter_ct const& filter)
// Prints out all visible allocated memory blocks and labels.
//
// The current 'manipulator' functions are:
//
// - void move_outside(marker_ct* marker, void const* ptr)
// Move `ptr' outside the list of `marker'.
// - void make_invisible(void const* ptr)
// Makes `ptr' invisible by deleting the corresponding alloc_ct
// object. `find_alloc' will return NULL for this allocation
// and the allocation will not show up in memory allocation
// overviews.
// - void make_all_allocations_invisible_except(void* ptr)
// For internal use.
//
#define LIBCWD_DEBUGMALLOC_INTERNAL
#include "sys.h"
#include <libcwd/config.h>
#if CWDEBUG_ALLOC || defined(LIBCWD_DOXYGEN)
#include <cstring>
#include <string>
#include <map>
#include <new>
#include <csignal>
#include <inttypes.h>
#ifdef HAVE_POSIX_MEMALIGN
#include <cerrno> // For EINVAL and ENOMEM
#endif
#ifdef HAVE_DLOPEN
#include <dlfcn.h>
#endif
#include <execinfo.h> // For backtrace().
#if LIBCWD_THREAD_SAFE
// We got the C++ config earlier by <bits/stl_alloc.h>.
#ifdef __STL_THREADS
#include <bits/stl_threads.h> // For interface to _STL_mutex_lock (node allocator lock)
#if defined(__STL_GTHREADS)
#include "bits/gthr.h"
#else
#error You have an unsupported configuraton of gcc. Please tell that you dont have gthreads along with the output of gcc -v to libcwd@alinoe.com.
#endif // __STL_GTHREADS
#endif // __STL_THREADS
#endif // LIBCWD_THREAD_SAFE
#include <iostream>
#include <iomanip>
#include <sys/time.h> // Needed for gettimeofday(2)
#include "cwd_debug.h"
#include "libcwd/debug.h"
#include "ios_base_Init.h"
#include "match.h"
#include "zone.h"
#include <libcwd/cwprint.h>
#include <libcwd/bfd.h>
#ifdef HAVE_POSIX_MEMALIGN
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <cstdlib>
#endif
#if defined(HAVE_MALLOC_H) && (defined(HAVE_MEMALIGN) || defined(HAVE_VALLOC))
#include <malloc.h>
#endif
#if defined(HAVE_UNISTD_H) && defined(HAVE_VALLOC)
#include <unistd.h> // This is what is needed for valloc(3) on FreeBSD. Also needed for sysconf.
#endif
#if LIBCWD_THREAD_SAFE
#if CWDEBUG_DEBUGT
#define UNSET_TARGETHREAD __libcwd_tsd.target_thread = NULL
#else
#define UNSET_TARGETHREAD
#endif
#include <libcwd/private_mutex.inl>
using libcwd::_private_::rwlock_tct;
using libcwd::_private_::mutex_tct;
using libcwd::_private_::mutex_ct;
using libcwd::_private_::threadlist_t;
using libcwd::_private_::threadlist;
using libcwd::_private_::location_cache_instance;
using libcwd::_private_::list_allocations_instance;
using libcwd::_private_::threadlist_instance;
using libcwd::_private_::dlclose_instance;
using libcwd::_private_::backtrace_instance;
// We can't use a read/write lock here because that leads to a deadlock.
// rwlocks have to use condition variables or semaphores and both try to get a
// (libpthread internal) self-lock that is already set by libthread when it calls
// free() in order to destroy thread specific data 1st level arrays.
// Note that we use rwlock_tct<threadlist_instance>, but that is safe because
// the free() from __pthread_destroy_specifics (in the pthread library) will only
// be trying to free memory that was allocated (for glibc-2.2.4 at specific.c:109)
// by the same thread and thus will not attempt to acquire the rwlock.
#if !CWDEBUG_DEBUGT
#define ACQUIRE_WRITE_LOCK(tt) do { __libcwd_tsd.target_thread = tt; __libcwd_tsd.target_thread->thread_mutex.lock(); } while(0)
#define RELEASE_WRITE_LOCK do { __libcwd_tsd.target_thread->thread_mutex.unlock(); UNSET_TARGETHREAD; } while(0)
#define ACQUIRE_READ_LOCK(tt) do { __libcwd_tsd.target_thread = tt; __libcwd_tsd.target_thread->thread_mutex.lock(); } while(0)
#define RELEASE_READ_LOCK do { __libcwd_tsd.target_thread->thread_mutex.unlock(); UNSET_TARGETHREAD; } while(0)
#else
#define ACQUIRE_WRITE_LOCK(tt) do { __libcwd_tsd.target_thread = tt; __libcwd_tsd.target_thread->thread_mutex.lock(__libcwd_tsd); } while(0)
#define RELEASE_WRITE_LOCK do { __libcwd_tsd.target_thread->thread_mutex.unlock(__libcwd_tsd); UNSET_TARGETHREAD; } while(0)
#define ACQUIRE_READ_LOCK(tt) do { __libcwd_tsd.target_thread = tt; __libcwd_tsd.target_thread->thread_mutex.lock(__libcwd_tsd); } while(0)
#define RELEASE_READ_LOCK do { __libcwd_tsd.target_thread->thread_mutex.unlock(__libcwd_tsd); UNSET_TARGETHREAD; } while(0)
#endif
#define ACQUIRE_READ2WRITE_LOCK do { } while(0)
#define ACQUIRE_WRITE2READ_LOCK do { } while(0)
// We can rwlock_tct here, because this lock is never used from free(),
// only from new, new[], malloc and realloc.
#define ACQUIRE_LC_WRITE_LOCK rwlock_tct<location_cache_instance>::wrlock()
#define RELEASE_LC_WRITE_LOCK rwlock_tct<location_cache_instance>::wrunlock()
#define ACQUIRE_LC_READ_LOCK rwlock_tct<location_cache_instance>::rdlock()
#define RELEASE_LC_READ_LOCK rwlock_tct<location_cache_instance>::rdunlock()
#define ACQUIRE_LC_READ2WRITE_LOCK rwlock_tct<location_cache_instance>::rd2wrlock()
#define ACQUIRE_LC_WRITE2READ_LOCK rwlock_tct<location_cache_instance>::wr2rdlock()
#define DLCLOSE_ACQUIRE_LOCK mutex_tct<dlclose_instance>::lock()
#define DLCLOSE_RELEASE_LOCK mutex_tct<dlclose_instance>::unlock()
#define BACKTRACE_ACQUIRE_LOCK mutex_tct<backtrace_instance>::lock()
#define BACKTRACE_RELEASE_LOCK mutex_tct<backtrace_instance>::unlock()
#else // !LIBCWD_THREAD_SAFE
#define ACQUIRE_WRITE_LOCK(tt) do { } while(0)
#define RELEASE_WRITE_LOCK do { } while(0)
#define ACQUIRE_READ_LOCK(tt) do { } while(0)
#define RELEASE_READ_LOCK do { } while(0)
#define ACQUIRE_READ2WRITE_LOCK do { } while(0)
#define ACQUIRE_WRITE2READ_LOCK do { } while(0)
#define ACQUIRE_LC_WRITE_LOCK do { } while(0)
#define RELEASE_LC_WRITE_LOCK do { } while(0)
#define ACQUIRE_LC_READ_LOCK do { } while(0)
#define RELEASE_LC_READ_LOCK do { } while(0)
#define ACQUIRE_LC_READ2WRITE_LOCK do { } while(0)
#define ACQUIRE_LC_WRITE2READ_LOCK do { } while(0)
#define DLCLOSE_ACQUIRE_LOCK do { } while(0)
#define DLCLOSE_RELEASE_LOCK do { } while(0)
#define BACKTRACE_ACQUIRE_LOCK do { } while(0)
#define BACKTRACE_RELEASE_LOCK do { } while(0)
#endif // !LIBCWD_THREAD_SAFE
#if CWDEBUG_LOCATION
#define LIBCWD_COMMA_LOCATION(x) , x
#else
#define LIBCWD_COMMA_LOCATION(x)
#endif
#ifdef LIBCWD_DOXYGEN
// Doxygen doesn't parse the define below. On linux this evaluates to 0.
#define USE_DLOPEN_RATHER_THAN_MACROS_KLUDGE 0
#elif defined(LIBCWD_USE_EXTERNAL_C_LINKAGE_FOR_MALLOC) && defined(HAVE_DLOPEN) \
&& !defined(LIBCWD_HAVE__LIBC_MALLOC) && !defined(LIBCWD_HAVE___LIBC_MALLOC)
#define USE_DLOPEN_RATHER_THAN_MACROS_KLUDGE 1
#else
#define USE_DLOPEN_RATHER_THAN_MACROS_KLUDGE 0
#endif
#ifdef LIBCWD_USE_EXTERNAL_C_LINKAGE_FOR_MALLOC
#define __libcwd_malloc malloc
#define __libcwd_calloc calloc
#define __libcwd_realloc realloc
#define __libcwd_free free
#define dc_malloc dc::malloc
// This only works with a patched valgrind (So not for you).
// Also change the macro in threading.cc.
#define VALGRIND 0
#if defined(LIBCWD_HAVE__LIBC_MALLOC) || defined(LIBCWD_HAVE___LIBC_MALLOC)
#if VALGRIND
#define __libc_malloc valgrind_malloc
#define __libc_calloc valgrind_calloc
#define __libc_realloc valgrind_realloc
#define __libc_free valgrind_free
#else // !VALGRIND
#ifdef LIBCWD_HAVE__LIBC_MALLOC
#define __libc_malloc _libc_malloc
#define __libc_calloc _libc_calloc
#define __libc_realloc _libc_realloc
#define __libc_free _libc_free
#endif // LIBCWD_HAVE__LIBC_MALLOC
#endif // !VALGRIND
#else // USE_DLOPEN_RATHER_THAN_MACROS_KLUDGE
#ifndef HAVE_DLOPEN
#error "configure bug: macros are inconsistent"
#endif
#define __libc_malloc (*libc_malloc)
#define __libc_calloc (*libc_calloc)
#define __libc_realloc (*libc_realloc)
#define __libc_free (*libc_free)
namespace libcwd {
void* malloc_bootstrap1(size_t size);
void* calloc_bootstrap1(size_t nmemb, size_t size);
} // namespace libcwd
void* __libc_malloc(size_t size) = libcwd::malloc_bootstrap1;
void* __libc_calloc(size_t nmemb, size_t size) = libcwd::calloc_bootstrap1;
void* __libc_realloc(void* ptr, size_t size);
void __libc_free(void* ptr);
void (*libc_free_final)(void* ptr) = (void (*)(void*))0;
#endif // USE_DLOPEN_RATHER_THAN_MACROS_KLUDGE
#else // !LIBCWD_USE_EXTERNAL_C_LINKAGE_FOR_MALLOC
#define __libc_malloc malloc
#define __libc_calloc calloc
#define __libc_realloc realloc
#define __libc_free free
#define dc_malloc dc::__libcwd_malloc
#endif // !LIBCWD_USE_EXTERNAL_C_LINKAGE_FOR_MALLOC
#ifdef HAVE_DLOPEN
#ifdef HAVE_POSIX_MEMALIGN
#define __libcwd_posix_memalign posix_memalign
#define __libc_posix_memalign (*libc_posix_memalign)
int __libc_posix_memalign(void** memptr, size_t alignment, size_t size);
#endif
#ifdef HAVE_ALIGNED_ALLOC
#define __libcwd_aligned_alloc aligned_alloc
#define __libc_aligned_alloc (*libc_aligned_alloc)
void* __libc_aligned_alloc(size_t alignment, size_t size);
#endif
#ifdef HAVE_MEMALIGN
#define __libcwd_memalign memalign
#define __libc_memalign (*libc_memalign)
void* __libc_memalign(size_t boundary, size_t size);
#endif
#ifdef HAVE_VALLOC
#define __libcwd_valloc valloc
#define __libc_valloc (*libc_valloc)
void* __libc_valloc(size_t size);
#endif
#else // !HAVE_DLOPEN
#ifdef HAVE_POSIX_MEMALIGN
#define __libc_posix_memalign posix_memalign
#endif
#ifdef HAVE_ALIGNED_ALLOC
#define __libc_aligned_alloc aligned_alloc
#endif
#ifdef HAVE_MEMALIGN
#define __libc_memalign memalign
#endif
#ifdef HAVE_VALLOC
#define __libc_valloc valloc
#endif
#endif // !HAVE_DLOPEN
#if !USE_DLOPEN_RATHER_THAN_MACROS_KLUDGE
extern "C" void* __libc_malloc(size_t size) noexcept __attribute__((__malloc__));
extern "C" void* __libc_calloc(size_t nmemb, size_t size) noexcept __attribute__((__malloc__));
extern "C" void* __libc_realloc(void* ptr, size_t size) noexcept __attribute__((__malloc__));
extern "C" void __libc_free(void* ptr) noexcept;
#endif
#if defined(VALGRIND) && VALGRIND
void* valgrind_malloc(size_t) { return 0; }
void* valgrind_calloc(size_t, size_t) { return 0; }
void* valgrind_realloc(void*, size_t) { return 0; }
void valgrind_free(void*) { }
#endif
namespace libcwd {
namespace _private_ {
#if LIBCWD_THREAD_SAFE && CWDEBUG_DEBUGM
extern bool WST_multi_threaded;
#endif
void no_alloc_print_int_to(std::ostream* os, unsigned long val, bool hexadecimal)
{
char buf[32]; // 32 > x where x is the number of digits of the largest unsigned long.
char* p = &buf[32];
int base = hexadecimal ? 16 : 10;
do
{
int digit = val % base;
if (digit < 10)
*--p = (char)('0' + digit);
else
*--p = (char)('a' - 10 + digit);
val /= base;
}
while(val > 0);
if (hexadecimal)
{
*--p = 'x';
*--p = '0';
}
os->write(p, &buf[32] - p);
}
void smart_ptr::decrement(LIBCWD_TSD_PARAM)
{
if (M_string_literal)
return;
if (M_ptr && reinterpret_cast<refcnt_charptr_ct*>(M_ptr)->decrement())
{
set_alloc_checking_off(LIBCWD_TSD);
delete reinterpret_cast<refcnt_charptr_ct*>(M_ptr);
set_alloc_checking_on(LIBCWD_TSD);
}
}
void smart_ptr::copy_from(smart_ptr const& ptr)
{
if (M_ptr != ptr.M_ptr)
{
LIBCWD_TSD_DECLARATION;
decrement(LIBCWD_TSD);
M_ptr = ptr.M_ptr;
M_string_literal = ptr.M_string_literal;
increment();
}
}
void smart_ptr::copy_from(char const* ptr)
{
LIBCWD_TSD_DECLARATION;
decrement(LIBCWD_TSD);
M_string_literal = true;
M_ptr = const_cast<char*>(ptr);
}
void smart_ptr::copy_from(char* ptr)
{
LIBCWD_TSD_DECLARATION;
decrement(LIBCWD_TSD);
if (ptr)
{
set_alloc_checking_off(LIBCWD_TSD);
M_ptr = new refcnt_charptr_ct(ptr);
set_alloc_checking_on(LIBCWD_TSD);
M_string_literal = false;
}
else
{
M_ptr = NULL;
M_string_literal = true;
}
}
} // namespace _private_
extern void ST_initialize_globals(LIBCWD_TSD_PARAM);
} // namespace libcwd
#if CWDEBUG_DEBUGM
#define DEBUGDEBUG_DoutInternal_MARKER DEBUGDEBUG_CERR( "DoutInternal at " << __FILE__ << ':' << __LINE__ )
#define DEBUGDEBUG_DoutFatalInternal_MARKER DEBUGDEBUG_CERR( "DoutFatalInternal at " << __FILE__ << ':' << __LINE__ )
#define DEBUGDEBUG_ELSE_DoutInternal(data) else DEBUGDEBUG_CERR( "library_call == " << __libcwd_tsd.library_call << "; DoutInternal skipped for: " << data )
#define DEBUGDEBUG_ELSE_DoutFatalInternal(data) FATALDEBUGDEBUG_CERR( "library_call == " << __libcwd_tsd.library_call << "; DoutFatalInternal skipped for: " << data )
#else
#define DEBUGDEBUG_DoutInternal_MARKER
#define DEBUGDEBUG_DoutFatalInternal_MARKER
#define DEBUGDEBUG_ELSE_DoutInternal(data)
#define DEBUGDEBUG_ELSE_DoutFatalInternal(data)
#endif
#define DoutInternalDo( debug_object, cntrl, data ) \
do \
{ \
DEBUGDEBUG_DoutInternal_MARKER; \
if (__libcwd_tsd.library_call == 0 && LIBCWD_DO_TSD_MEMBER_OFF(debug_object) < 0) \
{ \
DEBUGDEBUG_CERR( "Entering 'DoutInternal(cntrl, \"" << data << "\")'. internal == " << __libcwd_tsd.internal << '.' ); \
channel_set_bootstrap_st channel_set(LIBCWD_DO_TSD(debug_object) LIBCWD_COMMA_TSD); \
bool on; \
{ \
using namespace channels; \
on = (channel_set|cntrl).on; \
} \
if (on) \
{ \
LIBCWD_DO_TSD(debug_object).start(debug_object, channel_set LIBCWD_COMMA_TSD); \
++ LIBCWD_DO_TSD_MEMBER_OFF(debug_object); \
_private_::no_alloc_ostream_ct no_alloc_ostream(*LIBCWD_DO_TSD_MEMBER(debug_object, current_bufferstream)); \
no_alloc_ostream << data; \
-- LIBCWD_DO_TSD_MEMBER_OFF(debug_object); \
LIBCWD_DO_TSD(debug_object).finish(debug_object, channel_set LIBCWD_COMMA_TSD); \
} \
DEBUGDEBUG_CERR( "Leaving 'DoutInternal(cntrl, \"" << data << "\")'. internal = " << __libcwd_tsd.internal << '.' ); \
} \
DEBUGDEBUG_ELSE_DoutInternal(data); \
} while(0)
#define DoutInternal( cntrl, data ) DoutInternalDo( libcw_do, cntrl, data ) \
#define DoutFatalInternal( cntrl, data ) \
do \
{ \
DEBUGDEBUG_DoutFatalInternal_MARKER; \
if (__libcwd_tsd.library_call < 2) \
{ \
DEBUGDEBUG_CERR( "Entering 'DoutFatalInternal(cntrl, \"" << data << "\")'. internal == " << \
__libcwd_tsd.internal << "; setting internal to 0." ); \
__libcwd_tsd.internal = 0; \
channel_set_bootstrap_fatal_st channel_set(LIBCWD_DO_TSD(libcw_do) LIBCWD_COMMA_TSD); \
{ \
using namespace channels; \
channel_set|cntrl; \
} \
LIBCWD_DO_TSD(libcw_do).start(libcw_do, channel_set LIBCWD_COMMA_TSD); \
++ LIBCWD_DO_TSD_MEMBER_OFF(libcw_do); \
_private_::no_alloc_ostream_ct no_alloc_ostream(*LIBCWD_DO_TSD_MEMBER(libcw_do, current_bufferstream)); \
no_alloc_ostream << data; \
-- LIBCWD_DO_TSD_MEMBER_OFF(libcw_do); \
LIBCWD_DO_TSD(libcw_do).fatal_finish(libcw_do, channel_set LIBCWD_COMMA_TSD); /* Never returns */ \
LIBCWD_DEBUG_ASSERT( !"Bug in libcwd!" ); \
core_dump(); \
} \
else \
{ \
DEBUGDEBUG_ELSE_DoutFatalInternal(data); \
LIBCWD_DEBUG_ASSERT( !"See msg above." ); \
core_dump(); \
} \
} while(0)
namespace libcwd {
namespace _private_ {
//
// The following is about tackling recursive calls.
//
// The memory allocations performed by the users application are stored
// in a red/black tree using the STL map container. Using this container
// does cause calls to malloc by itself. Of course we can not also store
// *those* allocations in the map, that would result in an endless loop.
// Therefore we use a local variable `internal' and set that before we
// do the calls to the map<> methods. Then, when malloc is called with
// `internal' set, we do not store these allocations in the map.
// Note that we need to know *precisely* which pointers point to these
// 'internal' allocations because we also need to set `internal' prior
// to freeing these blocks - otherwise an error would be detected because
// we can't find that pointer in the map (thinking the user is freeing
// memory that he never allocated).
//
// Apart from the above there is a second cause of recursive calls:
// Any library call (to libc or libstdc++) can in principle allocate or
// free memory. Writing debug output or writing directly to cerr also
// causes a lot of calls to operator new and operator new[]. But in this
// case we have no control from where these allocations are done, or from
// where they are freed. Therefore we assume that calls to Dout() and
// writing data to cerr is _always_ done with `internal' set off.
// That allows the user to use cerr too ;).
//
// This means however that it is possible to recursively enter the
// allocation routines with `internal' set off. In order to avoid an
// endless loop in this case we have to store those allocations in the
// map<> in total silence (no debug output what so ever). The variable
// that is used for this is `library_call'. In other words, `library_call'
// is set when we call a library function that could call malloc or new
// (most notably operator<<(ostream&, ...), while writing debug output;
// when creating a location_ct (which is a 'library call' of libcwd); or
// while doing string manipulations).
//
// internal library_call Action
//
// false false Called from user space; store in the map and write debug output.
// true false Called from our map<>, do not store in the map; allowed to write debug output.
// false true Called from a library call while in new or malloc etc.
// store in map but do not call any library function.
// true true Called from our map<> while storing an allocation that
// was done during a call to a library function from
// one of our allocation routines, do not store in the map
// and do not call any library functions.
#if LIBCWD_IOSBASE_INIT_ALLOCATES
//
// The following kludge is needed because of a bug in libstdc++-v3.
//
bool WST_ios_base_initialized = false; // MT-safe: this is set to true before main() is reached and
// never changed anymore (see `inside_ios_base_Init_Init').
// _private_::
bool inside_ios_base_Init_Init() // Single Threaded function.
{
LIBCWD_TSD_DECLARATION;
LIBCWD_DEBUGM_ASSERT(!__libcwd_tsd.internal);
#ifndef _GLIBCPP_USE_WCHAR_T
static std::ios_base::Init __dummy;
if (std::cerr.flags() != std::ios_base::unitbuf) // Still didn't reach the end of std::ios_base::Init::Init()?
#else
if (std::wcerr.flags() != std::ios_base::unitbuf)
#endif
return true;
WST_ios_base_initialized = true;
++LIBCWD_DO_TSD_MEMBER_OFF(libcw_do);
make_all_allocations_invisible_except(NULL); // Get rid of the <pre ios initialization> allocation list.
--LIBCWD_DO_TSD_MEMBER_OFF(libcw_do);
DEBUGDEBUG_CERR( "Standard streams initialized." );
return false;
}
#endif // LIBCWD_IOSBASE_INIT_ALLOCATES
#if CWDEBUG_DEBUGM
// _private_::
void set_alloc_checking_off(LIBCWD_TSD_PARAM)
{
LIBCWD_DEBUGM_CERR( "set_alloc_checking_off called from " << __builtin_return_address(0) << ": internal == " << __libcwd_tsd.internal << "; setting it to " << (__libcwd_tsd.internal + 1) << '.' );
++__libcwd_tsd.internal;
}
// _private_::
void set_alloc_checking_on(LIBCWD_TSD_PARAM)
{
if (__libcwd_tsd.internal == 0)
DoutFatal(dc::core, "Calling `set_alloc_checking_on' while ALREADY on.");
LIBCWD_DEBUGM_CERR( "set_alloc_checking_on called from " << __builtin_return_address(0) << ": internal == " << __libcwd_tsd.internal << "; setting it to " << (__libcwd_tsd.internal - 1) << '.' );
--__libcwd_tsd.internal;
}
// _private_::
int set_library_call_on(LIBCWD_TSD_PARAM)
{
int saved_internal = __libcwd_tsd.internal;
LIBCWD_DEBUGM_CERR( "set_library_call_on called from " << __builtin_return_address(0) << ": internal == " << __libcwd_tsd.internal << "; setting it to 0." );
__libcwd_tsd.internal = 0;
if (saved_internal == 0)
DoutFatal(dc::core, "Calling `set_library_call_on' while not internal.");
++__libcwd_tsd.library_call;
++LIBCWD_DO_TSD_MEMBER_OFF(libcw_do);
return saved_internal;
}
// _private_::
void set_library_call_off(int saved_internal LIBCWD_COMMA_TSD_PARAM)
{
--LIBCWD_DO_TSD_MEMBER_OFF(libcw_do);
if (__libcwd_tsd.internal)
DoutFatal(dc::core, "Calling `set_library_call_off' while internal?!");
if (__libcwd_tsd.library_call == 0)
DoutFatal(dc::core, "Calling `set_library_call_off' while library_call == 0 ?!");
LIBCWD_DEBUGM_CERR( "set_library_call_off called from " << __builtin_return_address(0) << ": internal == " << __libcwd_tsd.internal << "; setting it to " << saved_internal << '.' );
__libcwd_tsd.internal = saved_internal;
--__libcwd_tsd.library_call;
}
#endif // CWDEBUG_DEBUGM
} // namespace _private_
class dm_alloc_ct;
class memblk_key_ct;
class memblk_info_ct;
enum deallocated_from_nt { from_free, from_delete, from_delete_array, error };
// Indicates if 'internal_free()' was called via __libcwd_free(), 'operator delete()' or 'operator delete[]()'.
static deallocated_from_nt const expected_from[] = {
from_delete, // memblk_type_new
error, // memblk_type_deleted
from_delete_array, // memblk_type_new_array
error, // memblk_type_deleted_array
from_free, // memblk_type_malloc
from_free, // memblk_type_realloc
error, // memblk_type_freed
#if CWDEBUG_MARKER
from_delete, // memblk_type_marker
error, // memblk_type_deleted_marker
#endif
from_free, // memblk_type_posix_memalign
from_free, // memblk_type_aligned_alloc
from_free, // memblk_type_memalign
from_free, // memblk_type_valloc
from_free // memblk_type_external
};
/**
* \brief Allow writing a \c memblk_types_nt directly to an ostream.
*
* Writes the name of the \c memblk_types_nt \a memblk_type to \c ostream \a os.
*/
std::ostream& operator<<(std::ostream& os, memblk_types_nt memblk_type)
{
switch(memblk_type)
{
case memblk_type_new:
os << "memblk_type_new";
break;
case memblk_type_deleted:
os << "memblk_type_deleted";
break;
case memblk_type_new_array:
os << "memblk_type_new_array";
break;
case memblk_type_deleted_array:
os << "memblk_type_deleted_array";
break;
case memblk_type_malloc:
os << "memblk_type_malloc";
break;
case memblk_type_realloc:
os << "memblk_type_realloc";
break;
case memblk_type_freed:
os << "memblk_type_freed";
break;
#if CWDEBUG_MARKER
case memblk_type_marker:
os << "memblk_type_marker";
break;
case memblk_type_deleted_marker:
os << "memblk_type_deleted_marker";
break;
#endif
case memblk_type_posix_memalign:
os << "memblk_type_posix_memalign";
break;
case memblk_type_aligned_alloc:
os << "memblk_type_aligned_alloc";
break;
case memblk_type_memalign:
os << "memblk_type_memalign";
break;
case memblk_type_valloc:
os << "memblk_type_valloc";
break;
case memblk_type_external:
os << "memblk_type_external";
break;
}
return os;
}
// Used to print the label in an Memory Allocation Overview.
class memblk_types_label_ct {
private:
memblk_types_nt M_memblk_type;
public:
memblk_types_label_ct(memblk_types_nt memblk_type) : M_memblk_type(memblk_type) { }
void print_on(std::ostream& os) const;
};
void memblk_types_label_ct::print_on(std::ostream& os) const
{
switch (M_memblk_type)
{
case memblk_type_new:
os.write(" ", 10);
break;
case memblk_type_new_array:
os.write("new[] ", 10);
break;
case memblk_type_malloc:
os.write("malloc ", 10);
break;
case memblk_type_realloc:
os.write("realloc ", 10);
break;
#if CWDEBUG_MARKER
case memblk_type_marker:
os.write("(MARKER) ", 10);
break;
case memblk_type_deleted_marker:
#endif
case memblk_type_deleted:
case memblk_type_deleted_array:
os.write("(deleted) ", 10);
break;
case memblk_type_freed:
os.write("(freed) ", 10);
break;
case memblk_type_posix_memalign:
os.write("pmemalign ", 10);
break;
case memblk_type_aligned_alloc:
os.write("aligned_a ", 10);
break;
case memblk_type_memalign:
os.write("memalign ", 10);
break;
case memblk_type_valloc:
os.write("valloc ", 10);
break;
case memblk_type_external:
os.write("external ", 10);
break;
}
}
#if CWDEBUG_DEBUGM
_private_::raw_write_nt const& operator<<(_private_::raw_write_nt const& raw_write, memblk_key_ct const&)
{
write(2, "<memblk_key_ct>", 15);
return raw_write;
}
#endif
#if CWDEBUG_LOCATION && CWDEBUG_DEBUG
inline
_private_::raw_write_nt const&
operator<<(_private_::raw_write_nt const& os, location_ct const& location)
{
_private_::print_location_on(os, location);
return os;
}
#endif
class dm_alloc_base_ct : public alloc_ct {
public:
dm_alloc_base_ct(void const* s, size_t sz, memblk_types_nt type,
type_info_ct const& ti, struct timeval const& t LIBCWD_COMMA_LOCATION(location_ct const* l))
: alloc_ct(s, sz, type, ti, t LIBCWD_COMMA_LOCATION(l)) { }
void print_description(debug_ct& debug_object, alloc_filter_ct const& filter LIBCWD_COMMA_TSD_PARAM) const;
};
#if LIBCWD_THREAD_SAFE
#define BASE_ALLOC_LIST (__libcwd_tsd.target_thread->base_alloc_list)
#define CURRENT_ALLOC_LIST (__libcwd_tsd.target_thread->current_alloc_list)
#define CURRENT_OWNER_NODE (__libcwd_tsd.target_thread->current_owner_node)
#define MEMSIZE (__libcwd_tsd.target_thread->memsize)
#define MEMBLKS (__libcwd_tsd.target_thread->memblks)
#else // !LIBCWD_THREAD_SAFE
static dm_alloc_ct* ST_base_alloc_list = NULL; // The base list with `dm_alloc_ct' objects.
// Each of these objects has a list of it's own.
static dm_alloc_ct** ST_current_alloc_list = &ST_base_alloc_list; // The current list to which newly allocated memory blocks are added.
static dm_alloc_ct* ST_current_owner_node = NULL; // If the CURRENT_ALLOC_LIST != &BASE_ALLOC_LIST, then this variable
// points to the dm_alloc_ct node who owns the current list.
static size_t ST_memsize = 0; // Total number of allocated bytes (excluding internal allocations).
static unsigned long ST_memblks = 0; // Total number of allocated blocks (excluding internal allocations).
// Access macros for the single threaded case.
#define BASE_ALLOC_LIST ST_base_alloc_list
#define CURRENT_ALLOC_LIST ST_current_alloc_list
#define CURRENT_OWNER_NODE ST_current_owner_node
#define MEMSIZE ST_memsize
#define MEMBLKS ST_memblks
#endif // !LIBCWD_THREAD_SAFE
//===========================================================================
//
// class dm_alloc_ct
//
// The object that is stored in the `alloc list' (see 2) above).
// Each `memblk_ct' is related with exactly one `dm_alloc_ct' object.
// All `dm_alloc_ct' objects do is maintaining an extra relationship between
// certain `memblk_ct' objects. ('certain' because `memblk_ct' can be made
// 'invisible', in which case they do not participate in this relation and
// are never showed in any overview).
// Unfortunately the STL doesn't allow an object to be simultaneously a map and
// a list, therefore we have to implement our own list (prev/next pointers).
class dm_alloc_copy_ct; // A class to temporarily copy dm_alloc_ct objects to in order to print them.
class dm_alloc_ct : public dm_alloc_base_ct {
friend class dm_alloc_copy_ct;
#if CWDEBUG_MARKER
friend class marker_ct;
friend void move_outside(marker_ct* marker, void const* ptr);
#endif
private:
dm_alloc_ct* next; // Next memblk in list `my_list'
dm_alloc_ct* prev; // Previous memblk in list `my_list'
dm_alloc_ct* a_next_list; // First element of my childeren (if any)
dm_alloc_ct** my_list; // Pointer to my list, never NULL except after deinitialization.
dm_alloc_ct* my_owner_node; // Pointer to node who's a_next_list contains this object.
public:
dm_alloc_ct(void const* s, size_t sz, memblk_types_nt f, struct timeval const& t
LIBCWD_COMMA_TSD_PARAM LIBCWD_COMMA_LOCATION(location_ct const* l)) : // MT-safe: write lock is set.
dm_alloc_base_ct(s, sz , f, unknown_type_info_c, t LIBCWD_COMMA_LOCATION(l)), prev(NULL), a_next_list(NULL)
{
LIBCWD_DEBUGT_ASSERT(__libcwd_tsd.target_thread == &(*__libcwd_tsd.thread_iter));
next = *CURRENT_ALLOC_LIST;
my_list = CURRENT_ALLOC_LIST;
my_owner_node = CURRENT_OWNER_NODE;
dm_alloc_ct** foo = CURRENT_ALLOC_LIST;
*foo = this;
if (next)
next->prev = this;
MEMSIZE += sz;
++MEMBLKS;
}
// Constructor: Add `node' at the start of `list'
#if CWDEBUG_DEBUGM
dm_alloc_ct(dm_alloc_ct const& __dummy) : dm_alloc_base_ct(__dummy) { LIBCWD_TSD_DECLARATION; DoutFatalInternal( dc::fatal, "Calling dm_alloc_ct::dm_alloc_ct(dm_alloc_ct const&)" ); }
// No copy constructor allowed.
#endif
void deinit(LIBCWD_TSD_PARAM);
~dm_alloc_ct() { if (my_list) { LIBCWD_TSD_DECLARATION; deinit(LIBCWD_TSD); } }
void new_list(LIBCWD_TSD_PARAM) // MT-safe: write lock is set.
{
// A new list is always added for the current thread.
LIBCWD_DEBUGT_ASSERT(__libcwd_tsd.target_thread == &(*__libcwd_tsd.thread_iter));
CURRENT_ALLOC_LIST = &a_next_list;
CURRENT_OWNER_NODE = this;
}
// Start a new list in this node.
void change_flags(memblk_types_nt new_memblk_type) { a_memblk_type = new_memblk_type; }
void change_label(type_info_ct const& ti, _private_::smart_ptr description) { type_info_ptr = &ti; a_description = description; }
type_info_ct const* typeid_ptr() const { return type_info_ptr; }
_private_::smart_ptr description() const { return a_description; }
dm_alloc_ct const* next_node() const { return next; }
dm_alloc_ct const* prev_node() const { return prev; }
dm_alloc_ct const* next_list() const { return a_next_list; }
void const* start() const { return a_start; }
bool is_deleted() const;
size_t size() const { return a_size; }
void printOn(std::ostream& os) const;
static void descend_current_alloc_list(LIBCWD_TSD_PARAM); // MT-safe: write lock is set.
friend inline std::ostream& operator<<(std::ostream& os, dm_alloc_ct const& alloc) { alloc.printOn(os); return os; }
friend inline _private_::no_alloc_ostream_ct& operator<<(_private_::no_alloc_ostream_ct& os, dm_alloc_ct const& alloc) { alloc.printOn(os.M_os); return os; }
};
class dm_alloc_copy_ct : public dm_alloc_base_ct {
private:
dm_alloc_copy_ct* M_next;
dm_alloc_copy_ct* M_next_list;
public:
dm_alloc_copy_ct(dm_alloc_ct const& alloc) : dm_alloc_base_ct(alloc), M_next(NULL), M_next_list(NULL) { }
~dm_alloc_copy_ct();
static dm_alloc_copy_ct* deep_copy(dm_alloc_ct const* alloc);
unsigned long show_alloc_list(debug_ct& debug_object, int depth, channel_ct const& channel, alloc_filter_ct const& filter) const;
dm_alloc_copy_ct const* next() const { return M_next; }
};
dm_alloc_copy_ct* dm_alloc_copy_ct::deep_copy(dm_alloc_ct const* alloc)
{
dm_alloc_copy_ct* dm_alloc_copy = new dm_alloc_copy_ct(*alloc);
if (alloc->a_next_list)
dm_alloc_copy->M_next_list = deep_copy(alloc->a_next_list);
dm_alloc_copy_ct* prev = dm_alloc_copy;
for(;;)
{
alloc = alloc->next;
if (!alloc)
break;
prev->M_next = new dm_alloc_copy_ct(*alloc);
prev = prev->M_next;
if (alloc->a_next_list)
prev->M_next_list = dm_alloc_copy_ct::deep_copy(alloc->a_next_list);
}
return dm_alloc_copy;
}
dm_alloc_copy_ct::~dm_alloc_copy_ct()
{
delete M_next_list;
//delete M_next;
// In order to avoid a stack overflow in the case that this is
// a very long list, delete all M_next objects in the chain
// here (patch by Joel Nordell).
dm_alloc_copy_ct* next;
for (dm_alloc_copy_ct* p = M_next; p != NULL; p = next)
{
next = p->M_next;
p->M_next = NULL;
delete p;
}
}
typedef dm_alloc_ct const const_dm_alloc_ct;
// Set `CURRENT_ALLOC_LIST' back to its parent list.
void dm_alloc_ct::descend_current_alloc_list(LIBCWD_TSD_PARAM) // MT-safe: write lock is set.
{
if (CURRENT_OWNER_NODE)
{
CURRENT_ALLOC_LIST = CURRENT_OWNER_NODE->my_list;
CURRENT_OWNER_NODE = (*CURRENT_ALLOC_LIST)->my_owner_node;
}
else
CURRENT_ALLOC_LIST = &BASE_ALLOC_LIST;
}
//=============================================================================
//
// classes memblk_key_ct and memblk_info_ct
//
// The object memblk_ct (std::pair<memblk_key_ct const, memblk_info_ct) that is
// stored in the red/black tree.
// Each of these objects is related with exactly one allocated memory block.
// Note that ALL allocated memory blocks have a related `memblk_ct'
// unless they are allocated from within this file (with `internal' set).
struct appblock;
class memblk_key_ct {
private:
void const* a_start; // Start of allocated memory block
void const* a_end; // End of allocated memory block
public:
memblk_key_ct(appblock const* s, size_t size) : a_start(s), a_end(reinterpret_cast<char const*>(s) + size) { }
void const* start() const { return a_start; }
void const* end() const { return a_end; }
size_t size() const { return (char*)a_end - (char*)a_start; }
bool operator<(memblk_key_ct b) const
{ return a_end < b.start() || (a_end == b.start() && size() > 0); }
bool operator>(memblk_key_ct b) const