]> git.saurik.com Git - wxWidgets.git/blame - src/common/memory.cpp
Merge in from trunk r64802 - r68625
[wxWidgets.git] / src / common / memory.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
8898456d 2// Name: src/common/memory.cpp
c801d85f
KB
3// Purpose: Memory checking implementation
4// Author: Arthur Seaton, Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
55d99c7a 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
c801d85f
KB
10/////////////////////////////////////////////////////////////////////////////
11
c801d85f
KB
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
8898456d 16 #pragma hdrstop
c801d85f
KB
17#endif
18
4b6a582b 19#if wxUSE_MEMORY_TRACING || wxUSE_DEBUG_CONTEXT
c801d85f 20
e4db172a
WS
21#include "wx/memory.h"
22
c801d85f 23#ifndef WX_PRECOMP
57bd4c60
WS
24 #ifdef __WXMSW__
25 #include "wx/msw/wrapwin.h"
26 #endif
8898456d
WS
27 #include "wx/utils.h"
28 #include "wx/app.h"
29 #include "wx/hash.h"
e4db172a 30 #include "wx/log.h"
c801d85f
KB
31#endif
32
b5c70253 33#if wxUSE_THREADS
8898456d 34 #include "wx/thread.h"
b5c70253
JS
35#endif
36
c801d85f
KB
37#include <stdlib.h>
38
3f4a0c5b
VZ
39#include "wx/ioswrap.h"
40
6842d186 41#if !defined(__WATCOMC__) && !(defined(__VMS__) && ( __VMS_VER < 70000000 ) )\
f172cb82 42 && !defined( __MWERKS__ )
c801d85f
KB
43#include <memory.h>
44#endif
45
46#include <stdarg.h>
47#include <string.h>
48
4b6a582b 49#if wxUSE_THREADS
0fd28ea3
JS
50#define USE_THREADSAFE_MEMORY_ALLOCATION 1
51#else
52#define USE_THREADSAFE_MEMORY_ALLOCATION 0
53#endif
54
55
c801d85f
KB
56#ifdef new
57#undef new
58#endif
c801d85f
KB
59
60// wxDebugContext wxTheDebugContext;
61/*
62 Redefine new and delete so that we can pick up situations where:
3f4a0c5b
VZ
63 - we overwrite or underwrite areas of malloc'd memory.
64 - we use uninitialise variables
c801d85f
KB
65 Only do this in debug mode.
66
67 We change new to get enough memory to allocate a struct, followed
68 by the caller's requested memory, followed by a tag. The struct
69 is used to create a doubly linked list of these areas and also
70 contains another tag. The tags are used to determine when the area
71 has been over/under written.
72*/
73
74
75/*
76 Values which are used to set the markers which will be tested for
77 under/over write. There are 3 of these, one in the struct, one
78 immediately after the struct but before the caller requested memory and
79 one immediately after the requested memory.
80*/
81#define MemStartCheck 0x23A8
82#define MemMidCheck 0xA328
83#define MemEndCheck 0x8A32
84#define MemFillChar 0xAF
85#define MemStructId 0x666D
86
87/*
88 External interface for the wxMemStruct class. Others are
89 defined inline within the class def. Here we only need to be able
90 to add and delete nodes from the list and handle errors in some way.
91*/
92
93/*
94 Used for internal "this shouldn't happen" type of errors.
95*/
96void wxMemStruct::ErrorMsg (const char * mesg)
97{
77ffb593 98 wxLogMessage(wxT("wxWidgets memory checking error: %s"), mesg);
c801d85f 99 PrintNode ();
c801d85f
KB
100}
101
102/*
103 Used when we find an overwrite or an underwrite error.
104*/
105void wxMemStruct::ErrorMsg ()
106{
77ffb593 107 wxLogMessage(wxT("wxWidgets over/underwrite memory error:"));
c801d85f 108 PrintNode ();
c801d85f
KB
109}
110
111
112/*
113 We want to find out if pointers have been overwritten as soon as is
114 possible, so test everything before we dereference it. Of course it's still
115 quite possible that, if things have been overwritten, this function will
116 fall over, but the only way of dealing with that would cost too much in terms
117 of time.
118*/
119int wxMemStruct::AssertList ()
120{
192c3efa
PC
121 if ((wxDebugContext::GetHead() && !wxDebugContext::GetHead()->AssertIt()) ||
122 (wxDebugContext::GetTail() && !wxDebugContext::GetTail()->AssertIt()))
123 {
3f4a0c5b
VZ
124 ErrorMsg ("Head or tail pointers trashed");
125 return 0;
c801d85f
KB
126 }
127 return 1;
128}
129
130
131/*
132 Check that the thing we're pointing to has the correct id for a wxMemStruct
133 object and also that it's previous and next pointers are pointing at objects
134 which have valid ids.
135 This is definitely not perfect since we could fall over just trying to access
136 any of the slots which we use here, but I think it's about the best that I
137 can do without doing something like taking all new wxMemStruct pointers and
138 comparing them against all known pointer within the list and then only
139 doing this sort of check _after_ you've found the pointer in the list. That
140 would be safer, but also much more time consuming.
141*/
142int wxMemStruct::AssertIt ()
143{
144 return (m_id == MemStructId &&
3f4a0c5b
VZ
145 (m_prev == 0 || m_prev->m_id == MemStructId) &&
146 (m_next == 0 || m_next->m_id == MemStructId));
c801d85f
KB
147}
148
149
150/*
151 Additions are always at the tail of the list.
152 Returns 0 on error, non-zero on success.
153*/
154int wxMemStruct::Append ()
155{
156 if (! AssertList ())
3f4a0c5b 157 return 0;
c801d85f
KB
158
159 if (wxDebugContext::GetHead () == 0) {
3f4a0c5b
VZ
160 if (wxDebugContext::GetTail () != 0) {
161 ErrorMsg ("Null list should have a null tail pointer");
162 return 0;
163 }
164 (void) wxDebugContext::SetHead (this);
165 (void) wxDebugContext::SetTail (this);
c801d85f 166 } else {
3f4a0c5b
VZ
167 wxDebugContext::GetTail ()->m_next = this;
168 this->m_prev = wxDebugContext::GetTail ();
169 (void) wxDebugContext::SetTail (this);
c801d85f
KB
170 }
171 return 1;
172}
173
174
175/*
176 Don't actually free up anything here as the space which is used
177 by the node will be free'd up when the whole block is free'd.
178 Returns 0 on error, non-zero on success.
179*/
180int wxMemStruct::Unlink ()
181{
182 if (! AssertList ())
3f4a0c5b 183 return 0;
c801d85f
KB
184
185 if (wxDebugContext::GetHead () == 0 || wxDebugContext::GetTail () == 0) {
3f4a0c5b
VZ
186 ErrorMsg ("Trying to remove node from empty list");
187 return 0;
c801d85f
KB
188 }
189
190 // Handle the part of the list before this node.
191 if (m_prev == 0) {
3f4a0c5b
VZ
192 if (this != wxDebugContext::GetHead ()) {
193 ErrorMsg ("No previous node for non-head node");
194 return 0;
195 }
196 (void) wxDebugContext::SetHead (m_next);
c801d85f 197 } else {
3f4a0c5b
VZ
198 if (! m_prev->AssertIt ()) {
199 ErrorMsg ("Trashed previous pointer");
200 return 0;
201 }
202
203 if (m_prev->m_next != this) {
204 ErrorMsg ("List is inconsistent");
205 return 0;
206 }
207 m_prev->m_next = m_next;
c801d85f
KB
208 }
209
210 // Handle the part of the list after this node.
211 if (m_next == 0) {
3f4a0c5b
VZ
212 if (this != wxDebugContext::GetTail ()) {
213 ErrorMsg ("No next node for non-tail node");
214 return 0;
215 }
216 (void) wxDebugContext::SetTail (m_prev);
c801d85f 217 } else {
3f4a0c5b
VZ
218 if (! m_next->AssertIt ()) {
219 ErrorMsg ("Trashed next pointer");
220 return 0;
221 }
222
223 if (m_next->m_prev != this) {
224 ErrorMsg ("List is inconsistent");
225 return 0;
226 }
227 m_next->m_prev = m_prev;
c801d85f
KB
228 }
229
230 return 1;
231}
232
233
234
235/*
236 Checks a node and block of memory to see that the markers are still
237 intact.
238*/
239int wxMemStruct::CheckBlock ()
240{
241 int nFailures = 0;
242
243 if (m_firstMarker != MemStartCheck) {
3f4a0c5b
VZ
244 nFailures++;
245 ErrorMsg ();
c801d85f 246 }
3f4a0c5b 247
c801d85f
KB
248 char * pointer = wxDebugContext::MidMarkerPos ((char *) this);
249 if (* (wxMarkerType *) pointer != MemMidCheck) {
3f4a0c5b
VZ
250 nFailures++;
251 ErrorMsg ();
c801d85f 252 }
3f4a0c5b 253
c801d85f
KB
254 pointer = wxDebugContext::EndMarkerPos ((char *) this, RequestSize ());
255 if (* (wxMarkerType *) pointer != MemEndCheck) {
3f4a0c5b
VZ
256 nFailures++;
257 ErrorMsg ();
c801d85f 258 }
3f4a0c5b 259
c801d85f
KB
260 return nFailures;
261}
262
263
264/*
265 Check the list of nodes to see if they are all ok.
266*/
267int wxMemStruct::CheckAllPrevious ()
268{
269 int nFailures = 0;
3f4a0c5b 270
c801d85f 271 for (wxMemStruct * st = this->m_prev; st != 0; st = st->m_prev) {
3f4a0c5b
VZ
272 if (st->AssertIt ())
273 nFailures += st->CheckBlock ();
274 else
275 return -1;
c801d85f
KB
276 }
277
278 return nFailures;
279}
280
281
282/*
283 When we delete a node we set the id slot to a specific value and then test
284 against this to see if a nodes have been deleted previously. I don't
285 just set the entire memory to the fillChar because then I'd be overwriting
286 useful stuff like the vtbl which may be needed to output the error message
287 including the file name and line numbers. Without this info the whole point
288 of this class is lost!
289*/
290void wxMemStruct::SetDeleted ()
291{
292 m_id = MemFillChar;
293}
294
295int wxMemStruct::IsDeleted ()
296{
297 return (m_id == MemFillChar);
298}
299
300
301/*
302 Print out a single node. There are many far better ways of doing this
303 but this will suffice for now.
304*/
305void wxMemStruct::PrintNode ()
306{
307 if (m_isObject)
308 {
309 wxObject *obj = (wxObject *)m_actualData;
310 wxClassInfo *info = obj->GetClassInfo();
c801d85f 311
f97c9854
JS
312 // Let's put this in standard form so IDEs can load the file at the appropriate
313 // line
6f28211a 314 wxString msg;
184b5d99 315
f97c9854 316 if (m_fileName)
223d09f6 317 msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum);
f97c9854
JS
318
319 if (info && info->GetClassName())
184b5d99 320 msg += info->GetClassName();
f97c9854 321 else
223d09f6 322 msg += wxT("object");
184b5d99
JS
323
324 wxString msg2;
ced55544 325 msg2.Printf(wxT(" at 0x%lX, size %d"), (long)GetActualData(), (int)RequestSize());
184b5d99 326 msg += msg2;
f97c9854 327
5fa399c9 328 wxLogMessage(msg);
c801d85f
KB
329 }
330 else
331 {
6f28211a 332 wxString msg;
184b5d99 333
c801d85f 334 if (m_fileName)
223d09f6
KB
335 msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum);
336 msg += wxT("non-object data");
184b5d99 337 wxString msg2;
ced55544 338 msg2.Printf(wxT(" at 0x%lX, size %d\n"), (long)GetActualData(), (int)RequestSize());
184b5d99
JS
339 msg += msg2;
340
5fa399c9 341 wxLogMessage(msg);
c801d85f
KB
342 }
343}
344
345void wxMemStruct::Dump ()
346{
347 if (!ValidateNode()) return;
3f4a0c5b 348
c801d85f
KB
349 if (m_isObject)
350 {
351 wxObject *obj = (wxObject *)m_actualData;
c801d85f 352
6f28211a 353 wxString msg;
c801d85f 354 if (m_fileName)
223d09f6 355 msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum);
c801d85f 356
184b5d99
JS
357
358 /* TODO: We no longer have a stream (using wxLogDebug) so we can't dump it.
359 * Instead, do what wxObject::Dump does.
360 * What should we do long-term, eliminate Dumping? Or specify
361 * that MyClass::Dump should use wxLogDebug? Ugh.
c801d85f 362 obj->Dump(wxDebugContext::GetStream());
184b5d99
JS
363 */
364
365 if (obj->GetClassInfo() && obj->GetClassInfo()->GetClassName())
366 msg += obj->GetClassInfo()->GetClassName();
367 else
223d09f6 368 msg += wxT("unknown object class");
184b5d99 369
6f28211a 370 wxString msg2;
ced55544 371 msg2.Printf(wxT(" at 0x%lX, size %d"), (long)GetActualData(), (int)RequestSize());
184b5d99
JS
372 msg += msg2;
373
aa75d6d9 374 wxDebugContext::OutputDumpLine(msg.c_str());
c801d85f
KB
375 }
376 else
377 {
6f28211a 378 wxString msg;
c801d85f 379 if (m_fileName)
223d09f6 380 msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum);
184b5d99 381
6f28211a 382 wxString msg2;
ced55544 383 msg2.Printf(wxT("non-object data at 0x%lX, size %d"), (long)GetActualData(), (int)RequestSize() );
184b5d99 384 msg += msg2;
aa75d6d9 385 wxDebugContext::OutputDumpLine(msg.c_str());
c801d85f
KB
386 }
387}
388
389
390/*
391 Validate a node. Check to see that the node is "clean" in the sense
392 that nothing has over/underwritten it etc.
393*/
394int wxMemStruct::ValidateNode ()
395{
396 char * startPointer = (char *) this;
397 if (!AssertIt ()) {
3f4a0c5b
VZ
398 if (IsDeleted ())
399 ErrorMsg ("Object already deleted");
400 else {
401 // Can't use the error routines as we have no recognisable object.
bd7d06f2 402#ifndef __WXGTK__
5fa399c9 403 wxLogMessage(wxT("Can't verify memory struct - all bets are off!"));
bd7d06f2 404#endif
3f4a0c5b
VZ
405 }
406 return 0;
c801d85f
KB
407 }
408
409/*
410 int i;
411 for (i = 0; i < wxDebugContext::TotSize (requestSize ()); i++)
412 cout << startPointer [i];
413 cout << endl;
414*/
415 if (Marker () != MemStartCheck)
416 ErrorMsg ();
417 if (* (wxMarkerType *) wxDebugContext::MidMarkerPos (startPointer) != MemMidCheck)
418 ErrorMsg ();
419 if (* (wxMarkerType *) wxDebugContext::EndMarkerPos (startPointer,
3f4a0c5b
VZ
420 RequestSize ()) !=
421 MemEndCheck)
c801d85f
KB
422 ErrorMsg ();
423
424 // Back to before the extra buffer and check that
425 // we can still read what we originally wrote.
426 if (Marker () != MemStartCheck ||
3f4a0c5b
VZ
427 * (wxMarkerType *) wxDebugContext::MidMarkerPos (startPointer)
428 != MemMidCheck ||
429 * (wxMarkerType *) wxDebugContext::EndMarkerPos (startPointer,
430 RequestSize ()) != MemEndCheck)
c801d85f 431 {
3f4a0c5b
VZ
432 ErrorMsg ();
433 return 0;
c801d85f
KB
434 }
435
436 return 1;
437}
438
439/*
440 The wxDebugContext class.
441*/
442
443wxMemStruct *wxDebugContext::m_head = NULL;
444wxMemStruct *wxDebugContext::m_tail = NULL;
c801d85f 445
6f28211a 446bool wxDebugContext::m_checkPrevious = false;
c801d85f 447int wxDebugContext::debugLevel = 1;
6f28211a 448bool wxDebugContext::debugOn = true;
c801d85f
KB
449wxMemStruct *wxDebugContext::checkPoint = NULL;
450
dc1efb1d
JS
451// For faster alignment calculation
452static wxMarkerType markerCalc[2];
453int wxDebugContext::m_balign = (int)((char *)&markerCalc[1] - (char*)&markerCalc[0]);
454int wxDebugContext::m_balignmask = (int)((char *)&markerCalc[1] - (char*)&markerCalc[0]) - 1;
455
6dfbea27
VZ
456// Pointer to global function to call at shutdown
457wxShutdownNotifyFunction wxDebugContext::sm_shutdownFn;
458
c801d85f
KB
459wxDebugContext::wxDebugContext(void)
460{
c801d85f
KB
461}
462
463wxDebugContext::~wxDebugContext(void)
464{
c801d85f 465}
c801d85f
KB
466
467/*
468 Work out the positions of the markers by creating an array of 2 markers
469 and comparing the addresses of the 2 elements. Use this number as the
470 alignment for markers.
471*/
472size_t wxDebugContext::CalcAlignment ()
473{
474 wxMarkerType ar[2];
475 return (char *) &ar[1] - (char *) &ar[0];
476}
477
478
479char * wxDebugContext::StructPos (const char * buf)
480{
481 return (char *) buf;
482}
483
484char * wxDebugContext::MidMarkerPos (const char * buf)
485{
486 return StructPos (buf) + PaddedSize (sizeof (wxMemStruct));
487}
488
489char * wxDebugContext::CallerMemPos (const char * buf)
490{
491 return MidMarkerPos (buf) + PaddedSize (sizeof(wxMarkerType));
492}
493
494
495char * wxDebugContext::EndMarkerPos (const char * buf, const size_t size)
496{
497 return CallerMemPos (buf) + PaddedSize (size);
498}
499
500
501/*
502 Slightly different as this takes a pointer to the start of the caller
503 requested region and returns a pointer to the start of the buffer.
504 */
505char * wxDebugContext::StartPos (const char * caller)
506{
507 return ((char *) (caller - wxDebugContext::PaddedSize (sizeof(wxMarkerType)) -
3f4a0c5b 508 wxDebugContext::PaddedSize (sizeof (wxMemStruct))));
c801d85f
KB
509}
510
511/*
512 We may need padding between various parts of the allocated memory.
513 Given a size of memory, this returns the amount of memory which should
514 be allocated in order to allow for alignment of the following object.
515
516 I don't know how portable this stuff is, but it seems to work for me at
517 the moment. It would be real nice if I knew more about this!
dc1efb1d
JS
518
519 // Note: this function is now obsolete (along with CalcAlignment)
520 // because the calculations are done statically, for greater speed.
c801d85f
KB
521*/
522size_t wxDebugContext::GetPadding (const size_t size)
523{
524 size_t pad = size % CalcAlignment ();
525 return (pad) ? sizeof(wxMarkerType) - pad : 0;
526}
527
c801d85f
KB
528size_t wxDebugContext::PaddedSize (const size_t size)
529{
dc1efb1d
JS
530 // Added by Terry Farnham <TJRT@pacbell.net> to replace
531 // slow GetPadding call.
478e6b71 532 int padb;
dc1efb1d 533
478e6b71
VZ
534 padb = size & m_balignmask;
535 if(padb)
536 return(size + m_balign - padb);
537 else
538 return(size);
c801d85f
KB
539}
540
541/*
542 Returns the total amount of memory which we need to get from the system
543 in order to satisfy a caller request. This includes space for the struct
544 plus markers and the caller's memory as well.
545*/
546size_t wxDebugContext::TotSize (const size_t reqSize)
547{
548 return (PaddedSize (sizeof (wxMemStruct)) + PaddedSize (reqSize) +
3f4a0c5b 549 2 * sizeof(wxMarkerType));
c801d85f
KB
550}
551
552
553/*
554 Traverse the list of nodes executing the given function on each node.
555*/
556void wxDebugContext::TraverseList (PmSFV func, wxMemStruct *from)
557{
558 if (!from)
559 from = wxDebugContext::GetHead ();
3f4a0c5b 560
2db0bbde
JS
561 wxMemStruct * st = NULL;
562 for (st = from; st != 0; st = st->m_next)
c801d85f
KB
563 {
564 void* data = st->GetActualData();
184b5d99
JS
565// if ((data != (void*)m_debugStream) && (data != (void*) m_streamBuf))
566 if (data != (void*) wxLog::GetActiveTarget())
c801d85f
KB
567 {
568 (st->*func) ();
569 }
570 }
571}
572
573
574/*
575 Print out the list.
576 */
577bool wxDebugContext::PrintList (void)
578{
d3b9f782 579 TraverseList ((PmSFV)&wxMemStruct::PrintNode, (checkPoint ? checkPoint->m_next : NULL));
c801d85f 580
6f28211a 581 return true;
c801d85f
KB
582}
583
584bool wxDebugContext::Dump(void)
585{
c801d85f 586 {
aa75d6d9 587 const wxChar* appName = wxT("application");
6f28211a 588 wxString appNameStr;
c801d85f
KB
589 if (wxTheApp)
590 {
591 appNameStr = wxTheApp->GetAppName();
550cb72b 592 appName = appNameStr.c_str();
5c33522f 593 OutputDumpLine(wxT("----- Memory dump of %s at %s -----"), appName, static_cast<const wxChar *>(wxNow().c_str()));
e55ad60e
RR
594 }
595 else
596 {
ced55544 597 OutputDumpLine( wxT("----- Memory dump -----") );
c801d85f
KB
598 }
599 }
f13b39c0 600
d3b9f782 601 TraverseList ((PmSFV)&wxMemStruct::Dump, (checkPoint ? checkPoint->m_next : NULL));
3f4a0c5b 602
6f28211a
DS
603 OutputDumpLine(wxEmptyString);
604 OutputDumpLine(wxEmptyString);
c801d85f 605
6f28211a 606 return true;
c801d85f
KB
607}
608
609struct wxDebugStatsStruct
610{
611 long instanceCount;
612 long totalSize;
4de6207a 613 wxChar *instanceClass;
c801d85f
KB
614 wxDebugStatsStruct *next;
615};
616
4de6207a 617static wxDebugStatsStruct *FindStatsStruct(wxDebugStatsStruct *st, wxChar *name)
c801d85f
KB
618{
619 while (st)
620 {
4de6207a 621 if (wxStrcmp(st->instanceClass, name) == 0)
c801d85f
KB
622 return st;
623 st = st->next;
624 }
625 return NULL;
626}
627
628static wxDebugStatsStruct *InsertStatsStruct(wxDebugStatsStruct *head, wxDebugStatsStruct *st)
629{
630 st->next = head;
631 return st;
632}
633
634bool wxDebugContext::PrintStatistics(bool detailed)
635{
e55ad60e 636 {
aa75d6d9 637 const wxChar* appName = wxT("application");
6f28211a 638 wxString appNameStr;
e55ad60e
RR
639 if (wxTheApp)
640 {
641 appNameStr = wxTheApp->GetAppName();
550cb72b 642 appName = appNameStr.c_str();
5c33522f 643 OutputDumpLine(wxT("----- Memory statistics of %s at %s -----"), appName, static_cast<const wxChar *>(wxNow().c_str()));
e55ad60e
RR
644 }
645 else
646 {
ced55544 647 OutputDumpLine( wxT("----- Memory statistics -----") );
e55ad60e
RR
648 }
649 }
3f4a0c5b 650
c801d85f 651 bool currentMode = GetDebugMode();
6f28211a 652 SetDebugMode(false);
3f4a0c5b 653
c801d85f
KB
654 long noNonObjectNodes = 0;
655 long noObjectNodes = 0;
656 long totalSize = 0;
657
658 wxDebugStatsStruct *list = NULL;
659
d3b9f782 660 wxMemStruct *from = (checkPoint ? checkPoint->m_next : NULL );
c801d85f
KB
661 if (!from)
662 from = wxDebugContext::GetHead ();
663
3f4a0c5b 664 wxMemStruct *st;
c801d85f
KB
665 for (st = from; st != 0; st = st->m_next)
666 {
667 void* data = st->GetActualData();
b5c70253 668 if (detailed && (data != (void*) wxLog::GetActiveTarget()))
c801d85f 669 {
ba14d986 670 wxChar *className = (wxChar*) wxT("nonobject");
c801d85f
KB
671 if (st->m_isObject && st->GetActualData())
672 {
673 wxObject *obj = (wxObject *)st->GetActualData();
674 if (obj->GetClassInfo()->GetClassName())
abe5726a 675 className = (wxChar*)obj->GetClassInfo()->GetClassName();
c801d85f
KB
676 }
677 wxDebugStatsStruct *stats = FindStatsStruct(list, className);
678 if (!stats)
679 {
680 stats = (wxDebugStatsStruct *)malloc(sizeof(wxDebugStatsStruct));
681 stats->instanceClass = className;
682 stats->instanceCount = 0;
683 stats->totalSize = 0;
684 list = InsertStatsStruct(list, stats);
685 }
686 stats->instanceCount ++;
687 stats->totalSize += st->RequestSize();
688 }
689
184b5d99 690 if (data != (void*) wxLog::GetActiveTarget())
c801d85f
KB
691 {
692 totalSize += st->RequestSize();
693 if (st->m_isObject)
694 noObjectNodes ++;
695 else
696 noNonObjectNodes ++;
697 }
698 }
699
700 if (detailed)
701 {
702 while (list)
703 {
ced55544 704 OutputDumpLine(wxT("%ld objects of class %s, total size %ld"),
c801d85f
KB
705 list->instanceCount, list->instanceClass, list->totalSize);
706 wxDebugStatsStruct *old = list;
707 list = old->next;
708 free((char *)old);
709 }
6f28211a 710 OutputDumpLine(wxEmptyString);
c801d85f 711 }
3f4a0c5b 712
c801d85f
KB
713 SetDebugMode(currentMode);
714
ced55544
VS
715 OutputDumpLine(wxT("Number of object items: %ld"), noObjectNodes);
716 OutputDumpLine(wxT("Number of non-object items: %ld"), noNonObjectNodes);
717 OutputDumpLine(wxT("Total allocated size: %ld"), totalSize);
6f28211a
DS
718 OutputDumpLine(wxEmptyString);
719 OutputDumpLine(wxEmptyString);
c801d85f 720
6f28211a 721 return true;
3f4a0c5b 722}
c801d85f
KB
723
724bool wxDebugContext::PrintClasses(void)
725{
c801d85f 726 {
aa75d6d9 727 const wxChar* appName = wxT("application");
6f28211a 728 wxString appNameStr;
c801d85f
KB
729 if (wxTheApp)
730 {
731 appNameStr = wxTheApp->GetAppName();
550cb72b 732 appName = appNameStr.c_str();
5fa399c9 733 wxLogMessage(wxT("----- Classes in %s -----"), appName);
c801d85f
KB
734 }
735 }
736
737 int n = 0;
644cb537 738 const wxClassInfo *info;
f4a8c29f 739
644cb537
MB
740 for (wxClassInfo::const_iterator node = wxClassInfo::begin_classinfo(),
741 end = wxClassInfo::end_classinfo();
742 node != end; ++node)
c801d85f 743 {
644cb537 744 info = *node;
c801d85f
KB
745 if (info->GetClassName())
746 {
184b5d99 747 wxString msg(info->GetClassName());
223d09f6 748 msg += wxT(" ");
184b5d99
JS
749
750 if (info->GetBaseClassName1() && !info->GetBaseClassName2())
751 {
223d09f6 752 msg += wxT("is a ");
184b5d99
JS
753 msg += info->GetBaseClassName1();
754 }
755 else if (info->GetBaseClassName1() && info->GetBaseClassName2())
756 {
223d09f6 757 msg += wxT("is a ");
184b5d99 758 msg += info->GetBaseClassName1() ;
223d09f6 759 msg += wxT(", ");
184b5d99
JS
760 msg += info->GetBaseClassName2() ;
761 }
762 if (info->GetConstructor())
223d09f6 763 msg += wxT(": dynamic");
184b5d99 764
5fa399c9 765 wxLogMessage(msg);
c801d85f 766 }
c801d85f
KB
767 n ++;
768 }
6f28211a 769 wxLogMessage(wxEmptyString);
5fa399c9 770 wxLogMessage(wxT("There are %d classes derived from wxObject."), n);
6f28211a
DS
771 wxLogMessage(wxEmptyString);
772 wxLogMessage(wxEmptyString);
773 return true;
3f4a0c5b 774}
c801d85f
KB
775
776void wxDebugContext::SetCheckpoint(bool all)
777{
778 if (all)
779 checkPoint = NULL;
780 else
781 checkPoint = m_tail;
782}
783
784// Checks all nodes since checkpoint, or since start.
785int wxDebugContext::Check(bool checkAll)
786{
787 int nFailures = 0;
3f4a0c5b 788
d3b9f782 789 wxMemStruct *from = (checkPoint ? checkPoint->m_next : NULL );
c801d85f
KB
790 if (!from || checkAll)
791 from = wxDebugContext::GetHead ();
792
793 for (wxMemStruct * st = from; st != 0; st = st->m_next)
794 {
795 if (st->AssertIt ())
796 nFailures += st->CheckBlock ();
797 else
798 return -1;
799 }
800
801 return nFailures;
802}
803
804// Count the number of non-wxDebugContext-related objects
805// that are outstanding
4fabb575 806int wxDebugContext::CountObjectsLeft(bool sinceCheckpoint)
c801d85f
KB
807{
808 int n = 0;
4fabb575
JS
809
810 wxMemStruct *from = NULL;
811 if (sinceCheckpoint && checkPoint)
812 from = checkPoint->m_next;
9a29912f 813 else
4fabb575 814 from = wxDebugContext::GetHead () ;
c801d85f
KB
815
816 for (wxMemStruct * st = from; st != 0; st = st->m_next)
817 {
818 void* data = st->GetActualData();
184b5d99 819 if (data != (void*) wxLog::GetActiveTarget())
c801d85f
KB
820 n ++;
821 }
822
823 return n ;
824}
825
ced55544
VS
826// This function is used to output the dump
827void wxDebugContext::OutputDumpLine(const wxChar *szFormat, ...)
828{
829 // a buffer of 2048 bytes should be long enough for a file name
830 // and a class name
831 wxChar buf[2048];
832 int count;
833 va_list argptr;
834 va_start(argptr, szFormat);
9a83f860 835 buf[WXSIZEOF(buf)-1] = wxT('\0');
ced55544
VS
836
837 // keep 3 bytes for a \r\n\0
9611b797 838 count = wxVsnprintf(buf, WXSIZEOF(buf)-3, szFormat, argptr);
ced55544
VS
839
840 if ( count < 0 )
9611b797 841 count = WXSIZEOF(buf)-3;
9a83f860
VZ
842 buf[count]=wxT('\r');
843 buf[count+1]=wxT('\n');
844 buf[count+2]=wxT('\0');
ced55544
VS
845
846 wxMessageOutputDebug dbgout;
847 dbgout.Printf(buf);
848}
849
6dfbea27
VZ
850void wxDebugContext::SetShutdownNotifyFunction(wxShutdownNotifyFunction shutdownFn)
851{
852 sm_shutdownFn = shutdownFn;
853}
854
ced55544 855
0fd28ea3 856#if USE_THREADSAFE_MEMORY_ALLOCATION
6f28211a 857static bool memSectionOk = false;
b5c70253
JS
858
859class MemoryCriticalSection : public wxCriticalSection
860{
861public:
ce1c6745
DS
862 MemoryCriticalSection() {
863 memSectionOk = true;
864 }
a83ea9c1
JS
865 ~MemoryCriticalSection() {
866 memSectionOk = false;
867 }
b5c70253
JS
868};
869
870class MemoryCriticalSectionLocker
871{
872public:
873 inline MemoryCriticalSectionLocker(wxCriticalSection& critsect)
ce1c6745 874 : m_critsect(critsect), m_locked(memSectionOk) { if(m_locked) m_critsect.Enter(); }
b5c70253 875 inline ~MemoryCriticalSectionLocker() { if(m_locked) m_critsect.Leave(); }
f13b39c0 876
b5c70253
JS
877private:
878 // no assignment operator nor copy ctor
879 MemoryCriticalSectionLocker(const MemoryCriticalSectionLocker&);
880 MemoryCriticalSectionLocker& operator=(const MemoryCriticalSectionLocker&);
f13b39c0 881
b5c70253 882 wxCriticalSection& m_critsect;
ce1c6745 883 bool m_locked;
b5c70253
JS
884};
885
a83ea9c1 886static MemoryCriticalSection memLocker;
aaf1bbfd 887
766a1d00
VZ
888#endif // USE_THREADSAFE_MEMORY_ALLOCATION
889
aaf1bbfd 890
766a1d00 891#if !(defined(__WXMSW__) && (defined(WXUSINGDLL) || defined(WXMAKINGDLL_BASE)))
fa6ca47e 892#if wxUSE_GLOBAL_MEMORY_OPERATORS
aaf1bbfd
JS
893void * operator new (size_t size, wxChar * fileName, int lineNum)
894{
895 return wxDebugAlloc(size, fileName, lineNum, false, false);
3d2eaa5d
DS
896}
897
aaf1bbfd
JS
898void * operator new (size_t size)
899{
900 return wxDebugAlloc(size, NULL, 0, false);
901}
902
903void operator delete (void * buf)
904{
905 wxDebugFree(buf, false);
906}
907
908#if wxUSE_ARRAY_MEMORY_OPERATORS
909void * operator new[] (size_t size)
910{
911 return wxDebugAlloc(size, NULL, 0, false, true);
912}
913
914void * operator new[] (size_t size, wxChar * fileName, int lineNum)
915{
916 return wxDebugAlloc(size, fileName, lineNum, false, true);
917}
918
919void operator delete[] (void * buf)
920{
921 wxDebugFree(buf, true);
922}
241421a6 923#endif // wxUSE_ARRAY_MEMORY_OPERATORS
5a7d1ad8 924#endif // wxUSE_GLOBAL_MEMORY_OPERATORS
766a1d00 925#endif // !(defined(__WXMSW__) && (defined(WXUSINGDLL) || defined(WXMAKINGDLL_BASE)))
b5c70253 926
c801d85f 927// TODO: store whether this is a vector or not.
4de6207a 928void * wxDebugAlloc(size_t size, wxChar * fileName, int lineNum, bool isObject, bool WXUNUSED(isVect) )
c801d85f 929{
0fd28ea3 930#if USE_THREADSAFE_MEMORY_ALLOCATION
aaf1bbfd 931 MemoryCriticalSectionLocker lock(memLocker);
b5c70253 932#endif
f13b39c0 933
c801d85f
KB
934 // If not in debugging allocation mode, do the normal thing
935 // so we don't leave any trace of ourselves in the node list.
936
86b3203f
DW
937#if defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 )
938// VA 3.0 still has trouble in here
939 return (void *)malloc(size);
940#endif
c801d85f
KB
941 if (!wxDebugContext::GetDebugMode())
942 {
943 return (void *)malloc(size);
944 }
3f4a0c5b 945
94b49b93
JS
946 int totSize = wxDebugContext::TotSize (size);
947 char * buf = (char *) malloc(totSize);
c801d85f 948 if (!buf) {
5fa399c9 949 wxLogMessage(wxT("Call to malloc (%ld) failed."), (long)size);
3f4a0c5b 950 return 0;
c801d85f
KB
951 }
952 wxMemStruct * st = (wxMemStruct *)buf;
953 st->m_firstMarker = MemStartCheck;
954 st->m_reqSize = size;
955 st->m_fileName = fileName;
956 st->m_lineNum = lineNum;
957 st->m_id = MemStructId;
958 st->m_prev = 0;
959 st->m_next = 0;
960 st->m_isObject = isObject;
961
962 // Errors from Append() shouldn't really happen - but just in case!
963 if (st->Append () == 0) {
3f4a0c5b 964 st->ErrorMsg ("Trying to append new node");
c801d85f 965 }
3f4a0c5b 966
c801d85f 967 if (wxDebugContext::GetCheckPrevious ()) {
3f4a0c5b
VZ
968 if (st->CheckAllPrevious () < 0) {
969 st->ErrorMsg ("Checking previous nodes");
970 }
c801d85f 971 }
3f4a0c5b 972
c801d85f
KB
973 // Set up the extra markers at the middle and end.
974 char * ptr = wxDebugContext::MidMarkerPos (buf);
975 * (wxMarkerType *) ptr = MemMidCheck;
976 ptr = wxDebugContext::EndMarkerPos (buf, size);
977 * (wxMarkerType *) ptr = MemEndCheck;
978
979 // pointer returned points to the start of the caller's
980 // usable area.
981 void *m_actualData = (void *) wxDebugContext::CallerMemPos (buf);
982 st->m_actualData = m_actualData;
983
984 return m_actualData;
985}
986
987// TODO: check whether was allocated as a vector
bd7d06f2 988void wxDebugFree(void * buf, bool WXUNUSED(isVect) )
c801d85f 989{
0fd28ea3 990#if USE_THREADSAFE_MEMORY_ALLOCATION
aaf1bbfd 991 MemoryCriticalSectionLocker lock(memLocker);
b5c70253 992#endif
f13b39c0 993
c801d85f
KB
994 if (!buf)
995 return;
3f4a0c5b 996
86b3203f
DW
997#if defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 )
998// VA 3.0 still has trouble in here
999 free((char *)buf);
1000#endif
c801d85f
KB
1001 // If not in debugging allocation mode, do the normal thing
1002 // so we don't leave any trace of ourselves in the node list.
1003 if (!wxDebugContext::GetDebugMode())
1004 {
1005 free((char *)buf);
1006 return;
1007 }
1008
1009 // Points to the start of the entire allocated area.
1010 char * startPointer = wxDebugContext::StartPos ((char *) buf);
1011 // Find the struct and make sure that it's identifiable.
1012 wxMemStruct * st = (wxMemStruct *) wxDebugContext::StructPos (startPointer);
1013
1014 if (! st->ValidateNode ())
3f4a0c5b 1015 return;
c801d85f
KB
1016
1017 // If this is the current checkpoint, we need to
1018 // move the checkpoint back so it points to a valid
1019 // node.
1020 if (st == wxDebugContext::checkPoint)
1021 wxDebugContext::checkPoint = wxDebugContext::checkPoint->m_prev;
1022
1023 if (! st->Unlink ())
1024 {
1025 st->ErrorMsg ("Unlinking deleted node");
1026 }
3f4a0c5b 1027
c801d85f
KB
1028 // Now put in the fill char into the id slot and the caller requested
1029 // memory locations.
1030 st->SetDeleted ();
1031 (void) memset (wxDebugContext::CallerMemPos (startPointer), MemFillChar,
3f4a0c5b 1032 st->RequestSize ());
c801d85f 1033
c801d85f
KB
1034 free((char *)st);
1035}
1036
1037// Trace: send output to the current debugging stream
33ac7e6f 1038void wxTrace(const wxChar * ...)
c801d85f 1039{
f6bcfd97
BP
1040#if 1
1041 wxFAIL_MSG(wxT("wxTrace is now obsolete. Please use wxDebugXXX instead."));
1042#else
1043 va_list ap;
4de6207a 1044 static wxChar buffer[512];
c801d85f
KB
1045
1046 va_start(ap, fmt);
1047
2049ba38 1048#ifdef __WXMSW__
c801d85f
KB
1049 wvsprintf(buffer,fmt,ap) ;
1050#else
1051 vsprintf(buffer,fmt,ap) ;
1052#endif
1053
1054 va_end(ap);
1055
1056 if (wxDebugContext::HasStream())
1057 {
1058 wxDebugContext::GetStream() << buffer;
1059 wxDebugContext::GetStream().flush();
1060 }
1061 else
2049ba38 1062#ifdef __WXMSW__
25889d3c 1063#ifdef __WIN32__
4de6207a 1064 OutputDebugString((LPCTSTR)buffer) ;
25889d3c
JS
1065#else
1066 OutputDebugString((const char*) buffer) ;
1067#endif
c801d85f
KB
1068#else
1069 fprintf(stderr, buffer);
1070#endif
f6bcfd97 1071#endif
c801d85f
KB
1072}
1073
1074// Trace with level
33ac7e6f 1075void wxTraceLevel(int, const wxChar * ...)
c801d85f 1076{
f6bcfd97
BP
1077#if 1
1078 wxFAIL_MSG(wxT("wxTrace is now obsolete. Please use wxDebugXXX instead."));
1079#else
c801d85f
KB
1080 if (wxDebugContext::GetLevel() < level)
1081 return;
3f4a0c5b 1082
c801d85f 1083 va_list ap;
4de6207a 1084 static wxChar buffer[512];
c801d85f
KB
1085
1086 va_start(ap, fmt);
1087
2049ba38 1088#ifdef __WXMSW__
f6bcfd97 1089 wxWvsprintf(buffer,fmt,ap) ;
c801d85f
KB
1090#else
1091 vsprintf(buffer,fmt,ap) ;
1092#endif
1093
1094 va_end(ap);
1095
1096 if (wxDebugContext::HasStream())
1097 {
1098 wxDebugContext::GetStream() << buffer;
1099 wxDebugContext::GetStream().flush();
1100 }
1101 else
2049ba38 1102#ifdef __WXMSW__
25889d3c 1103#ifdef __WIN32__
4de6207a 1104 OutputDebugString((LPCTSTR)buffer) ;
25889d3c
JS
1105#else
1106 OutputDebugString((const char*) buffer) ;
1107#endif
c801d85f
KB
1108#else
1109 fprintf(stderr, buffer);
1110#endif
f6bcfd97 1111#endif
c801d85f
KB
1112}
1113
ced55544 1114//----------------------------------------------------------------------------
df1f4832 1115// Final cleanup after all global objects in all files have been destroyed
ced55544
VS
1116//----------------------------------------------------------------------------
1117
1118// Don't set it to 0 by dynamic initialization
df1f4832 1119// Some compilers will really do the assignment later
ced55544
VS
1120// All global variables are initialized to 0 at the very beginning, and this is just fine.
1121int wxDebugContextDumpDelayCounter::sm_count;
1122
6dfbea27
VZ
1123wxDebugContextDumpDelayCounter::wxDebugContextDumpDelayCounter()
1124{
1125 sm_count++;
1126}
1127
1128wxDebugContextDumpDelayCounter::~wxDebugContextDumpDelayCounter()
1129{
1130 if ( !--sm_count )
1131 {
1132 // Notify app if we've been asked to do that
1133 if( wxDebugContext::sm_shutdownFn )
1134 wxDebugContext::sm_shutdownFn();
1135 DoDump();
1136 }
1137}
1138
ced55544
VS
1139void wxDebugContextDumpDelayCounter::DoDump()
1140{
6f28211a 1141 if (wxDebugContext::CountObjectsLeft(true) > 0)
ced55544
VS
1142 {
1143 wxDebugContext::OutputDumpLine(wxT("There were memory leaks.\n"));
1144 wxDebugContext::Dump();
1145 wxDebugContext::PrintStatistics();
1146 }
1147}
1148
1149// Even if there is nothing else, make sure that there is at
df1f4832 1150// least one cleanup counter object
ced55544
VS
1151static wxDebugContextDumpDelayCounter wxDebugContextDumpDelayCounter_One;
1152
4b6a582b 1153#endif // wxUSE_MEMORY_TRACING || wxUSE_DEBUG_CONTEXT