1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Memory checking implementation
4 // Author: Arthur Seaton, Julian Smart
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "memory.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
27 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
30 // #pragma implementation
39 #include "wx/thread.h"
45 #include "wx/ioswrap.h"
56 #if !defined(__WATCOMC__) && !(defined(__VMS__) && ( __VMS_VER < 70000000 ) )\
57 && !defined( __MWERKS__ ) && !defined(__SALFORDC__)
77 #include "wx/memory.h"
83 // wxDebugContext wxTheDebugContext;
85 Redefine new and delete so that we can pick up situations where:
86 - we overwrite or underwrite areas of malloc'd memory.
87 - we use uninitialise variables
88 Only do this in debug mode.
90 We change new to get enough memory to allocate a struct, followed
91 by the caller's requested memory, followed by a tag. The struct
92 is used to create a doubly linked list of these areas and also
93 contains another tag. The tags are used to determine when the area
94 has been over/under written.
99 Values which are used to set the markers which will be tested for
100 under/over write. There are 3 of these, one in the struct, one
101 immediately after the struct but before the caller requested memory and
102 one immediately after the requested memory.
104 #define MemStartCheck 0x23A8
105 #define MemMidCheck 0xA328
106 #define MemEndCheck 0x8A32
107 #define MemFillChar 0xAF
108 #define MemStructId 0x666D
111 External interface for the wxMemStruct class. Others are
112 defined inline within the class def. Here we only need to be able
113 to add and delete nodes from the list and handle errors in some way.
117 Used for internal "this shouldn't happen" type of errors.
119 void wxMemStruct::ErrorMsg (const char * mesg
)
121 wxLogMessage(wxT("wxWindows memory checking error: %s"), mesg
);
126 Used when we find an overwrite or an underwrite error.
128 void wxMemStruct::ErrorMsg ()
130 wxLogMessage(wxT("wxWindows over/underwrite memory error:"));
136 We want to find out if pointers have been overwritten as soon as is
137 possible, so test everything before we dereference it. Of course it's still
138 quite possible that, if things have been overwritten, this function will
139 fall over, but the only way of dealing with that would cost too much in terms
142 int wxMemStruct::AssertList ()
144 if (wxDebugContext::GetHead () != 0 && ! (wxDebugContext::GetHead ())->AssertIt () ||
145 wxDebugContext::GetTail () != 0 && ! wxDebugContext::GetTail ()->AssertIt ()) {
146 ErrorMsg ("Head or tail pointers trashed");
154 Check that the thing we're pointing to has the correct id for a wxMemStruct
155 object and also that it's previous and next pointers are pointing at objects
156 which have valid ids.
157 This is definitely not perfect since we could fall over just trying to access
158 any of the slots which we use here, but I think it's about the best that I
159 can do without doing something like taking all new wxMemStruct pointers and
160 comparing them against all known pointer within the list and then only
161 doing this sort of check _after_ you've found the pointer in the list. That
162 would be safer, but also much more time consuming.
164 int wxMemStruct::AssertIt ()
166 return (m_id
== MemStructId
&&
167 (m_prev
== 0 || m_prev
->m_id
== MemStructId
) &&
168 (m_next
== 0 || m_next
->m_id
== MemStructId
));
173 Additions are always at the tail of the list.
174 Returns 0 on error, non-zero on success.
176 int wxMemStruct::Append ()
181 if (wxDebugContext::GetHead () == 0) {
182 if (wxDebugContext::GetTail () != 0) {
183 ErrorMsg ("Null list should have a null tail pointer");
186 (void) wxDebugContext::SetHead (this);
187 (void) wxDebugContext::SetTail (this);
189 wxDebugContext::GetTail ()->m_next
= this;
190 this->m_prev
= wxDebugContext::GetTail ();
191 (void) wxDebugContext::SetTail (this);
198 Don't actually free up anything here as the space which is used
199 by the node will be free'd up when the whole block is free'd.
200 Returns 0 on error, non-zero on success.
202 int wxMemStruct::Unlink ()
207 if (wxDebugContext::GetHead () == 0 || wxDebugContext::GetTail () == 0) {
208 ErrorMsg ("Trying to remove node from empty list");
212 // Handle the part of the list before this node.
214 if (this != wxDebugContext::GetHead ()) {
215 ErrorMsg ("No previous node for non-head node");
218 (void) wxDebugContext::SetHead (m_next
);
220 if (! m_prev
->AssertIt ()) {
221 ErrorMsg ("Trashed previous pointer");
225 if (m_prev
->m_next
!= this) {
226 ErrorMsg ("List is inconsistent");
229 m_prev
->m_next
= m_next
;
232 // Handle the part of the list after this node.
234 if (this != wxDebugContext::GetTail ()) {
235 ErrorMsg ("No next node for non-tail node");
238 (void) wxDebugContext::SetTail (m_prev
);
240 if (! m_next
->AssertIt ()) {
241 ErrorMsg ("Trashed next pointer");
245 if (m_next
->m_prev
!= this) {
246 ErrorMsg ("List is inconsistent");
249 m_next
->m_prev
= m_prev
;
258 Checks a node and block of memory to see that the markers are still
261 int wxMemStruct::CheckBlock ()
265 if (m_firstMarker
!= MemStartCheck
) {
270 char * pointer
= wxDebugContext::MidMarkerPos ((char *) this);
271 if (* (wxMarkerType
*) pointer
!= MemMidCheck
) {
276 pointer
= wxDebugContext::EndMarkerPos ((char *) this, RequestSize ());
277 if (* (wxMarkerType
*) pointer
!= MemEndCheck
) {
287 Check the list of nodes to see if they are all ok.
289 int wxMemStruct::CheckAllPrevious ()
293 for (wxMemStruct
* st
= this->m_prev
; st
!= 0; st
= st
->m_prev
) {
295 nFailures
+= st
->CheckBlock ();
305 When we delete a node we set the id slot to a specific value and then test
306 against this to see if a nodes have been deleted previously. I don't
307 just set the entire memory to the fillChar because then I'd be overwriting
308 useful stuff like the vtbl which may be needed to output the error message
309 including the file name and line numbers. Without this info the whole point
310 of this class is lost!
312 void wxMemStruct::SetDeleted ()
317 int wxMemStruct::IsDeleted ()
319 return (m_id
== MemFillChar
);
324 Print out a single node. There are many far better ways of doing this
325 but this will suffice for now.
327 void wxMemStruct::PrintNode ()
331 wxObject
*obj
= (wxObject
*)m_actualData
;
332 wxClassInfo
*info
= obj
->GetClassInfo();
334 // Let's put this in standard form so IDEs can load the file at the appropriate
336 wxString
msg(wxT(""));
339 msg
.Printf(wxT("%s(%d): "), m_fileName
, (int)m_lineNum
);
341 if (info
&& info
->GetClassName())
342 msg
+= info
->GetClassName();
344 msg
+= wxT("object");
347 msg2
.Printf(wxT(" at $%lX, size %d"), (long)GetActualData(), (int)RequestSize());
357 msg
.Printf(wxT("%s(%d): "), m_fileName
, (int)m_lineNum
);
358 msg
+= wxT("non-object data");
360 msg2
.Printf(wxT(" at $%lX, size %d\n"), (long)GetActualData(), (int)RequestSize());
367 void wxMemStruct::Dump ()
369 if (!ValidateNode()) return;
373 wxObject
*obj
= (wxObject
*)m_actualData
;
375 wxString
msg(wxT(""));
377 msg
.Printf(wxT("%s(%d): "), m_fileName
, (int)m_lineNum
);
380 /* TODO: We no longer have a stream (using wxLogDebug) so we can't dump it.
381 * Instead, do what wxObject::Dump does.
382 * What should we do long-term, eliminate Dumping? Or specify
383 * that MyClass::Dump should use wxLogDebug? Ugh.
384 obj->Dump(wxDebugContext::GetStream());
387 if (obj
->GetClassInfo() && obj
->GetClassInfo()->GetClassName())
388 msg
+= obj
->GetClassInfo()->GetClassName();
390 msg
+= wxT("unknown object class");
393 msg2
.Printf(wxT(" at $%lX, size %d"), (long)GetActualData(), (int)RequestSize());
400 wxString
msg(wxT(""));
402 msg
.Printf(wxT("%s(%d): "), m_fileName
, (int)m_lineNum
);
405 msg2
.Printf(wxT("non-object data at $%lX, size %d"), (long)GetActualData(), (int)RequestSize() );
413 Validate a node. Check to see that the node is "clean" in the sense
414 that nothing has over/underwritten it etc.
416 int wxMemStruct::ValidateNode ()
418 char * startPointer
= (char *) this;
421 ErrorMsg ("Object already deleted");
423 // Can't use the error routines as we have no recognisable object.
425 wxLogMessage(wxT("Can't verify memory struct - all bets are off!"));
433 for (i = 0; i < wxDebugContext::TotSize (requestSize ()); i++)
434 cout << startPointer [i];
437 if (Marker () != MemStartCheck
)
439 if (* (wxMarkerType
*) wxDebugContext::MidMarkerPos (startPointer
) != MemMidCheck
)
441 if (* (wxMarkerType
*) wxDebugContext::EndMarkerPos (startPointer
,
446 // Back to before the extra buffer and check that
447 // we can still read what we originally wrote.
448 if (Marker () != MemStartCheck
||
449 * (wxMarkerType
*) wxDebugContext::MidMarkerPos (startPointer
)
451 * (wxMarkerType
*) wxDebugContext::EndMarkerPos (startPointer
,
452 RequestSize ()) != MemEndCheck
)
462 The wxDebugContext class.
465 wxMemStruct
*wxDebugContext::m_head
= NULL
;
466 wxMemStruct
*wxDebugContext::m_tail
= NULL
;
468 bool wxDebugContext::m_checkPrevious
= FALSE
;
469 int wxDebugContext::debugLevel
= 1;
470 bool wxDebugContext::debugOn
= TRUE
;
471 wxMemStruct
*wxDebugContext::checkPoint
= NULL
;
473 // For faster alignment calculation
474 static wxMarkerType markerCalc
[2];
475 int wxDebugContext::m_balign
= (int)((char *)&markerCalc
[1] - (char*)&markerCalc
[0]);
476 int wxDebugContext::m_balignmask
= (int)((char *)&markerCalc
[1] - (char*)&markerCalc
[0]) - 1;
478 wxDebugContext::wxDebugContext(void)
482 wxDebugContext::~wxDebugContext(void)
487 Work out the positions of the markers by creating an array of 2 markers
488 and comparing the addresses of the 2 elements. Use this number as the
489 alignment for markers.
491 size_t wxDebugContext::CalcAlignment ()
494 return (char *) &ar
[1] - (char *) &ar
[0];
498 char * wxDebugContext::StructPos (const char * buf
)
503 char * wxDebugContext::MidMarkerPos (const char * buf
)
505 return StructPos (buf
) + PaddedSize (sizeof (wxMemStruct
));
508 char * wxDebugContext::CallerMemPos (const char * buf
)
510 return MidMarkerPos (buf
) + PaddedSize (sizeof(wxMarkerType
));
514 char * wxDebugContext::EndMarkerPos (const char * buf
, const size_t size
)
516 return CallerMemPos (buf
) + PaddedSize (size
);
521 Slightly different as this takes a pointer to the start of the caller
522 requested region and returns a pointer to the start of the buffer.
524 char * wxDebugContext::StartPos (const char * caller
)
526 return ((char *) (caller
- wxDebugContext::PaddedSize (sizeof(wxMarkerType
)) -
527 wxDebugContext::PaddedSize (sizeof (wxMemStruct
))));
531 We may need padding between various parts of the allocated memory.
532 Given a size of memory, this returns the amount of memory which should
533 be allocated in order to allow for alignment of the following object.
535 I don't know how portable this stuff is, but it seems to work for me at
536 the moment. It would be real nice if I knew more about this!
538 // Note: this function is now obsolete (along with CalcAlignment)
539 // because the calculations are done statically, for greater speed.
541 size_t wxDebugContext::GetPadding (const size_t size
)
543 size_t pad
= size
% CalcAlignment ();
544 return (pad
) ? sizeof(wxMarkerType
) - pad
: 0;
547 size_t wxDebugContext::PaddedSize (const size_t size
)
549 // Added by Terry Farnham <TJRT@pacbell.net> to replace
550 // slow GetPadding call.
553 padb
= size
& m_balignmask
;
555 return(size
+ m_balign
- padb
);
561 Returns the total amount of memory which we need to get from the system
562 in order to satisfy a caller request. This includes space for the struct
563 plus markers and the caller's memory as well.
565 size_t wxDebugContext::TotSize (const size_t reqSize
)
567 return (PaddedSize (sizeof (wxMemStruct
)) + PaddedSize (reqSize
) +
568 2 * sizeof(wxMarkerType
));
573 Traverse the list of nodes executing the given function on each node.
575 void wxDebugContext::TraverseList (PmSFV func
, wxMemStruct
*from
)
578 from
= wxDebugContext::GetHead ();
580 wxMemStruct
* st
= NULL
;
581 for (st
= from
; st
!= 0; st
= st
->m_next
)
583 void* data
= st
->GetActualData();
584 // if ((data != (void*)m_debugStream) && (data != (void*) m_streamBuf))
585 if (data
!= (void*) wxLog::GetActiveTarget())
596 bool wxDebugContext::PrintList (void)
599 TraverseList ((PmSFV
)&wxMemStruct::PrintNode
, (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
));
607 bool wxDebugContext::Dump(void)
611 wxChar
* appName
= (wxChar
*) wxT("application");
612 wxString
appNameStr("");
615 appNameStr
= wxTheApp
->GetAppName();
616 appName
= WXSTRINGCAST appNameStr
;
617 wxLogMessage(wxT("----- Memory dump of %s at %s -----"), appName
, WXSTRINGCAST
wxNow() );
621 wxLogMessage( wxT("----- Memory dump -----") );
625 TraverseList ((PmSFV
)&wxMemStruct::Dump
, (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
));
627 wxLogMessage( wxT("") );
628 wxLogMessage( wxT("") );
637 struct wxDebugStatsStruct
641 wxChar
*instanceClass
;
642 wxDebugStatsStruct
*next
;
645 static wxDebugStatsStruct
*FindStatsStruct(wxDebugStatsStruct
*st
, wxChar
*name
)
649 if (wxStrcmp(st
->instanceClass
, name
) == 0)
656 static wxDebugStatsStruct
*InsertStatsStruct(wxDebugStatsStruct
*head
, wxDebugStatsStruct
*st
)
663 bool wxDebugContext::PrintStatistics(bool detailed
)
667 wxChar
* appName
= (wxChar
*) wxT("application");
668 wxString
appNameStr(wxT(""));
671 appNameStr
= wxTheApp
->GetAppName();
672 appName
= WXSTRINGCAST appNameStr
;
673 wxLogMessage(wxT("----- Memory statistics of %s at %s -----"), appName
, WXSTRINGCAST
wxNow() );
677 wxLogMessage( wxT("----- Memory statistics -----") );
681 bool currentMode
= GetDebugMode();
684 long noNonObjectNodes
= 0;
685 long noObjectNodes
= 0;
688 wxDebugStatsStruct
*list
= NULL
;
690 wxMemStruct
*from
= (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
);
692 from
= wxDebugContext::GetHead ();
695 for (st
= from
; st
!= 0; st
= st
->m_next
)
697 void* data
= st
->GetActualData();
698 if (detailed
&& (data
!= (void*) wxLog::GetActiveTarget()))
700 wxChar
*className
= (wxChar
*) wxT("nonobject");
701 if (st
->m_isObject
&& st
->GetActualData())
703 wxObject
*obj
= (wxObject
*)st
->GetActualData();
704 if (obj
->GetClassInfo()->GetClassName())
705 className
= (wxChar
*)obj
->GetClassInfo()->GetClassName();
707 wxDebugStatsStruct
*stats
= FindStatsStruct(list
, className
);
710 stats
= (wxDebugStatsStruct
*)malloc(sizeof(wxDebugStatsStruct
));
711 stats
->instanceClass
= className
;
712 stats
->instanceCount
= 0;
713 stats
->totalSize
= 0;
714 list
= InsertStatsStruct(list
, stats
);
716 stats
->instanceCount
++;
717 stats
->totalSize
+= st
->RequestSize();
720 if (data
!= (void*) wxLog::GetActiveTarget())
722 totalSize
+= st
->RequestSize();
734 wxLogMessage(wxT("%ld objects of class %s, total size %ld"),
735 list
->instanceCount
, list
->instanceClass
, list
->totalSize
);
736 wxDebugStatsStruct
*old
= list
;
740 wxLogMessage(wxT(""));
743 SetDebugMode(currentMode
);
745 wxLogMessage(wxT("Number of object items: %ld"), noObjectNodes
);
746 wxLogMessage(wxT("Number of non-object items: %ld"), noNonObjectNodes
);
747 wxLogMessage(wxT("Total allocated size: %ld"), totalSize
);
748 wxLogMessage(wxT(""));
749 wxLogMessage(wxT(""));
758 bool wxDebugContext::PrintClasses(void)
761 wxChar
* appName
= (wxChar
*) wxT("application");
762 wxString
appNameStr(wxT(""));
765 appNameStr
= wxTheApp
->GetAppName();
766 appName
= WXSTRINGCAST appNameStr
;
767 wxLogMessage(wxT("----- Classes in %s -----"), appName
);
775 wxClassInfo::sm_classTable
->BeginFind();
776 node
= wxClassInfo::sm_classTable
->Next();
779 info
= (wxClassInfo
*)node
->Data();
780 if (info
->GetClassName())
782 wxString
msg(info
->GetClassName());
785 if (info
->GetBaseClassName1() && !info
->GetBaseClassName2())
788 msg
+= info
->GetBaseClassName1();
790 else if (info
->GetBaseClassName1() && info
->GetBaseClassName2())
793 msg
+= info
->GetBaseClassName1() ;
795 msg
+= info
->GetBaseClassName2() ;
797 if (info
->GetConstructor())
798 msg
+= wxT(": dynamic");
802 node
= wxClassInfo::sm_classTable
->Next();
805 wxLogMessage(wxT(""));
806 wxLogMessage(wxT("There are %d classes derived from wxObject."), n
);
807 wxLogMessage(wxT(""));
808 wxLogMessage(wxT(""));
812 void wxDebugContext::SetCheckpoint(bool all
)
820 // Checks all nodes since checkpoint, or since start.
821 int wxDebugContext::Check(bool checkAll
)
825 wxMemStruct
*from
= (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
);
826 if (!from
|| checkAll
)
827 from
= wxDebugContext::GetHead ();
829 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
832 nFailures
+= st
->CheckBlock ();
840 // Count the number of non-wxDebugContext-related objects
841 // that are outstanding
842 int wxDebugContext::CountObjectsLeft(bool sinceCheckpoint
)
846 wxMemStruct
*from
= NULL
;
847 if (sinceCheckpoint
&& checkPoint
)
848 from
= checkPoint
->m_next
;
850 from
= wxDebugContext::GetHead () ;
852 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
854 void* data
= st
->GetActualData();
855 if (data
!= (void*) wxLog::GetActiveTarget())
863 static bool memSectionOk
= false;
865 class MemoryCriticalSection
: public wxCriticalSection
868 MemoryCriticalSection() {
873 class MemoryCriticalSectionLocker
876 inline MemoryCriticalSectionLocker(wxCriticalSection
& critsect
)
877 : m_critsect(critsect
), m_locked(memSectionOk
) { if(m_locked
) m_critsect
.Enter(); }
878 inline ~MemoryCriticalSectionLocker() { if(m_locked
) m_critsect
.Leave(); }
881 // no assignment operator nor copy ctor
882 MemoryCriticalSectionLocker(const MemoryCriticalSectionLocker
&);
883 MemoryCriticalSectionLocker
& operator=(const MemoryCriticalSectionLocker
&);
885 wxCriticalSection
& m_critsect
;
889 static MemoryCriticalSection memLocker
;
892 // TODO: store whether this is a vector or not.
893 void * wxDebugAlloc(size_t size
, wxChar
* fileName
, int lineNum
, bool isObject
, bool WXUNUSED(isVect
) )
896 MemoryCriticalSectionLocker
lock(memLocker
);
899 // If not in debugging allocation mode, do the normal thing
900 // so we don't leave any trace of ourselves in the node list.
902 #if defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 )
903 // VA 3.0 still has trouble in here
904 return (void *)malloc(size
);
906 if (!wxDebugContext::GetDebugMode())
908 return (void *)malloc(size
);
911 int totSize
= wxDebugContext::TotSize (size
);
912 char * buf
= (char *) malloc(totSize
);
914 wxLogMessage(wxT("Call to malloc (%ld) failed."), (long)size
);
917 wxMemStruct
* st
= (wxMemStruct
*)buf
;
918 st
->m_firstMarker
= MemStartCheck
;
919 st
->m_reqSize
= size
;
920 st
->m_fileName
= fileName
;
921 st
->m_lineNum
= lineNum
;
922 st
->m_id
= MemStructId
;
925 st
->m_isObject
= isObject
;
927 // Errors from Append() shouldn't really happen - but just in case!
928 if (st
->Append () == 0) {
929 st
->ErrorMsg ("Trying to append new node");
932 if (wxDebugContext::GetCheckPrevious ()) {
933 if (st
->CheckAllPrevious () < 0) {
934 st
->ErrorMsg ("Checking previous nodes");
938 // Set up the extra markers at the middle and end.
939 char * ptr
= wxDebugContext::MidMarkerPos (buf
);
940 * (wxMarkerType
*) ptr
= MemMidCheck
;
941 ptr
= wxDebugContext::EndMarkerPos (buf
, size
);
942 * (wxMarkerType
*) ptr
= MemEndCheck
;
944 // pointer returned points to the start of the caller's
946 void *m_actualData
= (void *) wxDebugContext::CallerMemPos (buf
);
947 st
->m_actualData
= m_actualData
;
952 // TODO: check whether was allocated as a vector
953 void wxDebugFree(void * buf
, bool WXUNUSED(isVect
) )
956 MemoryCriticalSectionLocker
lock(memLocker
);
962 #if defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 )
963 // VA 3.0 still has trouble in here
966 // If not in debugging allocation mode, do the normal thing
967 // so we don't leave any trace of ourselves in the node list.
968 if (!wxDebugContext::GetDebugMode())
974 // Points to the start of the entire allocated area.
975 char * startPointer
= wxDebugContext::StartPos ((char *) buf
);
976 // Find the struct and make sure that it's identifiable.
977 wxMemStruct
* st
= (wxMemStruct
*) wxDebugContext::StructPos (startPointer
);
979 if (! st
->ValidateNode ())
982 // If this is the current checkpoint, we need to
983 // move the checkpoint back so it points to a valid
985 if (st
== wxDebugContext::checkPoint
)
986 wxDebugContext::checkPoint
= wxDebugContext::checkPoint
->m_prev
;
990 st
->ErrorMsg ("Unlinking deleted node");
993 // Now put in the fill char into the id slot and the caller requested
996 (void) memset (wxDebugContext::CallerMemPos (startPointer
), MemFillChar
,
1002 // Trace: send output to the current debugging stream
1003 void wxTrace(const wxChar
* ...)
1006 wxFAIL_MSG(wxT("wxTrace is now obsolete. Please use wxDebugXXX instead."));
1009 static wxChar buffer
[512];
1014 wvsprintf(buffer
,fmt
,ap
) ;
1016 vsprintf(buffer
,fmt
,ap
) ;
1021 if (wxDebugContext::HasStream())
1023 wxDebugContext::GetStream() << buffer
;
1024 wxDebugContext::GetStream().flush();
1029 OutputDebugString((LPCTSTR
)buffer
) ;
1031 OutputDebugString((const char*) buffer
) ;
1034 fprintf(stderr
, buffer
);
1040 void wxTraceLevel(int, const wxChar
* ...)
1043 wxFAIL_MSG(wxT("wxTrace is now obsolete. Please use wxDebugXXX instead."));
1045 if (wxDebugContext::GetLevel() < level
)
1049 static wxChar buffer
[512];
1054 wxWvsprintf(buffer
,fmt
,ap
) ;
1056 vsprintf(buffer
,fmt
,ap
) ;
1061 if (wxDebugContext::HasStream())
1063 wxDebugContext::GetStream() << buffer
;
1064 wxDebugContext::GetStream().flush();
1069 OutputDebugString((LPCTSTR
)buffer
) ;
1071 OutputDebugString((const char*) buffer
) ;
1074 fprintf(stderr
, buffer
);
1079 #else // wxUSE_MEMORY_TRACING && defined(__WXDEBUG__)
1080 void wxTrace(const char *WXUNUSED(fmt
) ...)
1084 void wxTraceLevel(int WXUNUSED(level
), const char *WXUNUSED(fmt
) ...)