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