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"
47 #if !defined(__WATCOMC__) && !(defined(__VMS__) && ( __VMS_VER < 70000000 ) )\
48 && !defined( __MWERKS__ ) && !defined(__SALFORDC__)
68 #include "wx/memory.h"
70 #if wxUSE_THREADS && defined(__WXDEBUG__) && !defined(__WXMAC__)
71 #define USE_THREADSAFE_MEMORY_ALLOCATION 1
73 #define USE_THREADSAFE_MEMORY_ALLOCATION 0
81 // wxDebugContext wxTheDebugContext;
83 Redefine new and delete so that we can pick up situations where:
84 - we overwrite or underwrite areas of malloc'd memory.
85 - we use uninitialise variables
86 Only do this in debug mode.
88 We change new to get enough memory to allocate a struct, followed
89 by the caller's requested memory, followed by a tag. The struct
90 is used to create a doubly linked list of these areas and also
91 contains another tag. The tags are used to determine when the area
92 has been over/under written.
97 Values which are used to set the markers which will be tested for
98 under/over write. There are 3 of these, one in the struct, one
99 immediately after the struct but before the caller requested memory and
100 one immediately after the requested memory.
102 #define MemStartCheck 0x23A8
103 #define MemMidCheck 0xA328
104 #define MemEndCheck 0x8A32
105 #define MemFillChar 0xAF
106 #define MemStructId 0x666D
109 External interface for the wxMemStruct class. Others are
110 defined inline within the class def. Here we only need to be able
111 to add and delete nodes from the list and handle errors in some way.
115 Used for internal "this shouldn't happen" type of errors.
117 void wxMemStruct::ErrorMsg (const char * mesg
)
119 wxLogMessage(wxT("wxWindows memory checking error: %s"), mesg
);
124 Used when we find an overwrite or an underwrite error.
126 void wxMemStruct::ErrorMsg ()
128 wxLogMessage(wxT("wxWindows over/underwrite memory error:"));
134 We want to find out if pointers have been overwritten as soon as is
135 possible, so test everything before we dereference it. Of course it's still
136 quite possible that, if things have been overwritten, this function will
137 fall over, but the only way of dealing with that would cost too much in terms
140 int wxMemStruct::AssertList ()
142 if (wxDebugContext::GetHead () != 0 && ! (wxDebugContext::GetHead ())->AssertIt () ||
143 wxDebugContext::GetTail () != 0 && ! wxDebugContext::GetTail ()->AssertIt ()) {
144 ErrorMsg ("Head or tail pointers trashed");
152 Check that the thing we're pointing to has the correct id for a wxMemStruct
153 object and also that it's previous and next pointers are pointing at objects
154 which have valid ids.
155 This is definitely not perfect since we could fall over just trying to access
156 any of the slots which we use here, but I think it's about the best that I
157 can do without doing something like taking all new wxMemStruct pointers and
158 comparing them against all known pointer within the list and then only
159 doing this sort of check _after_ you've found the pointer in the list. That
160 would be safer, but also much more time consuming.
162 int wxMemStruct::AssertIt ()
164 return (m_id
== MemStructId
&&
165 (m_prev
== 0 || m_prev
->m_id
== MemStructId
) &&
166 (m_next
== 0 || m_next
->m_id
== MemStructId
));
171 Additions are always at the tail of the list.
172 Returns 0 on error, non-zero on success.
174 int wxMemStruct::Append ()
179 if (wxDebugContext::GetHead () == 0) {
180 if (wxDebugContext::GetTail () != 0) {
181 ErrorMsg ("Null list should have a null tail pointer");
184 (void) wxDebugContext::SetHead (this);
185 (void) wxDebugContext::SetTail (this);
187 wxDebugContext::GetTail ()->m_next
= this;
188 this->m_prev
= wxDebugContext::GetTail ();
189 (void) wxDebugContext::SetTail (this);
196 Don't actually free up anything here as the space which is used
197 by the node will be free'd up when the whole block is free'd.
198 Returns 0 on error, non-zero on success.
200 int wxMemStruct::Unlink ()
205 if (wxDebugContext::GetHead () == 0 || wxDebugContext::GetTail () == 0) {
206 ErrorMsg ("Trying to remove node from empty list");
210 // Handle the part of the list before this node.
212 if (this != wxDebugContext::GetHead ()) {
213 ErrorMsg ("No previous node for non-head node");
216 (void) wxDebugContext::SetHead (m_next
);
218 if (! m_prev
->AssertIt ()) {
219 ErrorMsg ("Trashed previous pointer");
223 if (m_prev
->m_next
!= this) {
224 ErrorMsg ("List is inconsistent");
227 m_prev
->m_next
= m_next
;
230 // Handle the part of the list after this node.
232 if (this != wxDebugContext::GetTail ()) {
233 ErrorMsg ("No next node for non-tail node");
236 (void) wxDebugContext::SetTail (m_prev
);
238 if (! m_next
->AssertIt ()) {
239 ErrorMsg ("Trashed next pointer");
243 if (m_next
->m_prev
!= this) {
244 ErrorMsg ("List is inconsistent");
247 m_next
->m_prev
= m_prev
;
256 Checks a node and block of memory to see that the markers are still
259 int wxMemStruct::CheckBlock ()
263 if (m_firstMarker
!= MemStartCheck
) {
268 char * pointer
= wxDebugContext::MidMarkerPos ((char *) this);
269 if (* (wxMarkerType
*) pointer
!= MemMidCheck
) {
274 pointer
= wxDebugContext::EndMarkerPos ((char *) this, RequestSize ());
275 if (* (wxMarkerType
*) pointer
!= MemEndCheck
) {
285 Check the list of nodes to see if they are all ok.
287 int wxMemStruct::CheckAllPrevious ()
291 for (wxMemStruct
* st
= this->m_prev
; st
!= 0; st
= st
->m_prev
) {
293 nFailures
+= st
->CheckBlock ();
303 When we delete a node we set the id slot to a specific value and then test
304 against this to see if a nodes have been deleted previously. I don't
305 just set the entire memory to the fillChar because then I'd be overwriting
306 useful stuff like the vtbl which may be needed to output the error message
307 including the file name and line numbers. Without this info the whole point
308 of this class is lost!
310 void wxMemStruct::SetDeleted ()
315 int wxMemStruct::IsDeleted ()
317 return (m_id
== MemFillChar
);
322 Print out a single node. There are many far better ways of doing this
323 but this will suffice for now.
325 void wxMemStruct::PrintNode ()
329 wxObject
*obj
= (wxObject
*)m_actualData
;
330 wxClassInfo
*info
= obj
->GetClassInfo();
332 // Let's put this in standard form so IDEs can load the file at the appropriate
334 wxString
msg(wxT(""));
337 msg
.Printf(wxT("%s(%d): "), m_fileName
, (int)m_lineNum
);
339 if (info
&& info
->GetClassName())
340 msg
+= info
->GetClassName();
342 msg
+= wxT("object");
345 msg2
.Printf(wxT(" at $%lX, size %d"), (long)GetActualData(), (int)RequestSize());
355 msg
.Printf(wxT("%s(%d): "), m_fileName
, (int)m_lineNum
);
356 msg
+= wxT("non-object data");
358 msg2
.Printf(wxT(" at $%lX, size %d\n"), (long)GetActualData(), (int)RequestSize());
365 void wxMemStruct::Dump ()
367 if (!ValidateNode()) return;
371 wxObject
*obj
= (wxObject
*)m_actualData
;
373 wxString
msg(wxT(""));
375 msg
.Printf(wxT("%s(%d): "), m_fileName
, (int)m_lineNum
);
378 /* TODO: We no longer have a stream (using wxLogDebug) so we can't dump it.
379 * Instead, do what wxObject::Dump does.
380 * What should we do long-term, eliminate Dumping? Or specify
381 * that MyClass::Dump should use wxLogDebug? Ugh.
382 obj->Dump(wxDebugContext::GetStream());
385 if (obj
->GetClassInfo() && obj
->GetClassInfo()->GetClassName())
386 msg
+= obj
->GetClassInfo()->GetClassName();
388 msg
+= wxT("unknown object class");
391 msg2
.Printf(wxT(" at $%lX, size %d"), (long)GetActualData(), (int)RequestSize());
398 wxString
msg(wxT(""));
400 msg
.Printf(wxT("%s(%d): "), m_fileName
, (int)m_lineNum
);
403 msg2
.Printf(wxT("non-object data at $%lX, size %d"), (long)GetActualData(), (int)RequestSize() );
411 Validate a node. Check to see that the node is "clean" in the sense
412 that nothing has over/underwritten it etc.
414 int wxMemStruct::ValidateNode ()
416 char * startPointer
= (char *) this;
419 ErrorMsg ("Object already deleted");
421 // Can't use the error routines as we have no recognisable object.
423 wxLogMessage(wxT("Can't verify memory struct - all bets are off!"));
431 for (i = 0; i < wxDebugContext::TotSize (requestSize ()); i++)
432 cout << startPointer [i];
435 if (Marker () != MemStartCheck
)
437 if (* (wxMarkerType
*) wxDebugContext::MidMarkerPos (startPointer
) != MemMidCheck
)
439 if (* (wxMarkerType
*) wxDebugContext::EndMarkerPos (startPointer
,
444 // Back to before the extra buffer and check that
445 // we can still read what we originally wrote.
446 if (Marker () != MemStartCheck
||
447 * (wxMarkerType
*) wxDebugContext::MidMarkerPos (startPointer
)
449 * (wxMarkerType
*) wxDebugContext::EndMarkerPos (startPointer
,
450 RequestSize ()) != MemEndCheck
)
460 The wxDebugContext class.
463 wxMemStruct
*wxDebugContext::m_head
= NULL
;
464 wxMemStruct
*wxDebugContext::m_tail
= NULL
;
466 bool wxDebugContext::m_checkPrevious
= FALSE
;
467 int wxDebugContext::debugLevel
= 1;
468 bool wxDebugContext::debugOn
= TRUE
;
469 wxMemStruct
*wxDebugContext::checkPoint
= NULL
;
471 // For faster alignment calculation
472 static wxMarkerType markerCalc
[2];
473 int wxDebugContext::m_balign
= (int)((char *)&markerCalc
[1] - (char*)&markerCalc
[0]);
474 int wxDebugContext::m_balignmask
= (int)((char *)&markerCalc
[1] - (char*)&markerCalc
[0]) - 1;
476 wxDebugContext::wxDebugContext(void)
480 wxDebugContext::~wxDebugContext(void)
485 Work out the positions of the markers by creating an array of 2 markers
486 and comparing the addresses of the 2 elements. Use this number as the
487 alignment for markers.
489 size_t wxDebugContext::CalcAlignment ()
492 return (char *) &ar
[1] - (char *) &ar
[0];
496 char * wxDebugContext::StructPos (const char * buf
)
501 char * wxDebugContext::MidMarkerPos (const char * buf
)
503 return StructPos (buf
) + PaddedSize (sizeof (wxMemStruct
));
506 char * wxDebugContext::CallerMemPos (const char * buf
)
508 return MidMarkerPos (buf
) + PaddedSize (sizeof(wxMarkerType
));
512 char * wxDebugContext::EndMarkerPos (const char * buf
, const size_t size
)
514 return CallerMemPos (buf
) + PaddedSize (size
);
519 Slightly different as this takes a pointer to the start of the caller
520 requested region and returns a pointer to the start of the buffer.
522 char * wxDebugContext::StartPos (const char * caller
)
524 return ((char *) (caller
- wxDebugContext::PaddedSize (sizeof(wxMarkerType
)) -
525 wxDebugContext::PaddedSize (sizeof (wxMemStruct
))));
529 We may need padding between various parts of the allocated memory.
530 Given a size of memory, this returns the amount of memory which should
531 be allocated in order to allow for alignment of the following object.
533 I don't know how portable this stuff is, but it seems to work for me at
534 the moment. It would be real nice if I knew more about this!
536 // Note: this function is now obsolete (along with CalcAlignment)
537 // because the calculations are done statically, for greater speed.
539 size_t wxDebugContext::GetPadding (const size_t size
)
541 size_t pad
= size
% CalcAlignment ();
542 return (pad
) ? sizeof(wxMarkerType
) - pad
: 0;
545 size_t wxDebugContext::PaddedSize (const size_t size
)
547 // Added by Terry Farnham <TJRT@pacbell.net> to replace
548 // slow GetPadding call.
551 padb
= size
& m_balignmask
;
553 return(size
+ m_balign
- padb
);
559 Returns the total amount of memory which we need to get from the system
560 in order to satisfy a caller request. This includes space for the struct
561 plus markers and the caller's memory as well.
563 size_t wxDebugContext::TotSize (const size_t reqSize
)
565 return (PaddedSize (sizeof (wxMemStruct
)) + PaddedSize (reqSize
) +
566 2 * sizeof(wxMarkerType
));
571 Traverse the list of nodes executing the given function on each node.
573 void wxDebugContext::TraverseList (PmSFV func
, wxMemStruct
*from
)
576 from
= wxDebugContext::GetHead ();
578 wxMemStruct
* st
= NULL
;
579 for (st
= from
; st
!= 0; st
= st
->m_next
)
581 void* data
= st
->GetActualData();
582 // if ((data != (void*)m_debugStream) && (data != (void*) m_streamBuf))
583 if (data
!= (void*) wxLog::GetActiveTarget())
594 bool wxDebugContext::PrintList (void)
597 TraverseList ((PmSFV
)&wxMemStruct::PrintNode
, (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
));
605 bool wxDebugContext::Dump(void)
609 wxChar
* appName
= (wxChar
*) wxT("application");
610 wxString
appNameStr("");
613 appNameStr
= wxTheApp
->GetAppName();
614 appName
= WXSTRINGCAST appNameStr
;
615 wxLogMessage(wxT("----- Memory dump of %s at %s -----"), appName
, WXSTRINGCAST
wxNow() );
619 wxLogMessage( wxT("----- Memory dump -----") );
623 TraverseList ((PmSFV
)&wxMemStruct::Dump
, (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
));
625 wxLogMessage( wxT("") );
626 wxLogMessage( wxT("") );
635 struct wxDebugStatsStruct
639 wxChar
*instanceClass
;
640 wxDebugStatsStruct
*next
;
643 static wxDebugStatsStruct
*FindStatsStruct(wxDebugStatsStruct
*st
, wxChar
*name
)
647 if (wxStrcmp(st
->instanceClass
, name
) == 0)
654 static wxDebugStatsStruct
*InsertStatsStruct(wxDebugStatsStruct
*head
, wxDebugStatsStruct
*st
)
661 bool wxDebugContext::PrintStatistics(bool detailed
)
665 wxChar
* appName
= (wxChar
*) wxT("application");
666 wxString
appNameStr(wxT(""));
669 appNameStr
= wxTheApp
->GetAppName();
670 appName
= WXSTRINGCAST appNameStr
;
671 wxLogMessage(wxT("----- Memory statistics of %s at %s -----"), appName
, WXSTRINGCAST
wxNow() );
675 wxLogMessage( wxT("----- Memory statistics -----") );
679 bool currentMode
= GetDebugMode();
682 long noNonObjectNodes
= 0;
683 long noObjectNodes
= 0;
686 wxDebugStatsStruct
*list
= NULL
;
688 wxMemStruct
*from
= (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
);
690 from
= wxDebugContext::GetHead ();
693 for (st
= from
; st
!= 0; st
= st
->m_next
)
695 void* data
= st
->GetActualData();
696 if (detailed
&& (data
!= (void*) wxLog::GetActiveTarget()))
698 wxChar
*className
= (wxChar
*) wxT("nonobject");
699 if (st
->m_isObject
&& st
->GetActualData())
701 wxObject
*obj
= (wxObject
*)st
->GetActualData();
702 if (obj
->GetClassInfo()->GetClassName())
703 className
= (wxChar
*)obj
->GetClassInfo()->GetClassName();
705 wxDebugStatsStruct
*stats
= FindStatsStruct(list
, className
);
708 stats
= (wxDebugStatsStruct
*)malloc(sizeof(wxDebugStatsStruct
));
709 stats
->instanceClass
= className
;
710 stats
->instanceCount
= 0;
711 stats
->totalSize
= 0;
712 list
= InsertStatsStruct(list
, stats
);
714 stats
->instanceCount
++;
715 stats
->totalSize
+= st
->RequestSize();
718 if (data
!= (void*) wxLog::GetActiveTarget())
720 totalSize
+= st
->RequestSize();
732 wxLogMessage(wxT("%ld objects of class %s, total size %ld"),
733 list
->instanceCount
, list
->instanceClass
, list
->totalSize
);
734 wxDebugStatsStruct
*old
= list
;
738 wxLogMessage(wxT(""));
741 SetDebugMode(currentMode
);
743 wxLogMessage(wxT("Number of object items: %ld"), noObjectNodes
);
744 wxLogMessage(wxT("Number of non-object items: %ld"), noNonObjectNodes
);
745 wxLogMessage(wxT("Total allocated size: %ld"), totalSize
);
746 wxLogMessage(wxT(""));
747 wxLogMessage(wxT(""));
756 bool wxDebugContext::PrintClasses(void)
759 wxChar
* appName
= (wxChar
*) wxT("application");
760 wxString
appNameStr(wxT(""));
763 appNameStr
= wxTheApp
->GetAppName();
764 appName
= WXSTRINGCAST appNameStr
;
765 wxLogMessage(wxT("----- Classes in %s -----"), appName
);
773 wxClassInfo::sm_classTable
->BeginFind();
774 node
= wxClassInfo::sm_classTable
->Next();
777 info
= (wxClassInfo
*)node
->Data();
778 if (info
->GetClassName())
780 wxString
msg(info
->GetClassName());
783 if (info
->GetBaseClassName1() && !info
->GetBaseClassName2())
786 msg
+= info
->GetBaseClassName1();
788 else if (info
->GetBaseClassName1() && info
->GetBaseClassName2())
791 msg
+= info
->GetBaseClassName1() ;
793 msg
+= info
->GetBaseClassName2() ;
795 if (info
->GetConstructor())
796 msg
+= wxT(": dynamic");
800 node
= wxClassInfo::sm_classTable
->Next();
803 wxLogMessage(wxT(""));
804 wxLogMessage(wxT("There are %d classes derived from wxObject."), n
);
805 wxLogMessage(wxT(""));
806 wxLogMessage(wxT(""));
810 void wxDebugContext::SetCheckpoint(bool all
)
818 // Checks all nodes since checkpoint, or since start.
819 int wxDebugContext::Check(bool checkAll
)
823 wxMemStruct
*from
= (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
);
824 if (!from
|| checkAll
)
825 from
= wxDebugContext::GetHead ();
827 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
830 nFailures
+= st
->CheckBlock ();
838 // Count the number of non-wxDebugContext-related objects
839 // that are outstanding
840 int wxDebugContext::CountObjectsLeft(bool sinceCheckpoint
)
844 wxMemStruct
*from
= NULL
;
845 if (sinceCheckpoint
&& checkPoint
)
846 from
= checkPoint
->m_next
;
848 from
= wxDebugContext::GetHead () ;
850 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
852 void* data
= st
->GetActualData();
853 if (data
!= (void*) wxLog::GetActiveTarget())
860 #if USE_THREADSAFE_MEMORY_ALLOCATION
861 static bool memSectionOk
= FALSE
;
863 class MemoryCriticalSection
: public wxCriticalSection
866 MemoryCriticalSection() {
871 class MemoryCriticalSectionLocker
874 inline MemoryCriticalSectionLocker(wxCriticalSection
& critsect
)
875 : m_critsect(critsect
), m_locked(memSectionOk
) { if(m_locked
) m_critsect
.Enter(); }
876 inline ~MemoryCriticalSectionLocker() { if(m_locked
) m_critsect
.Leave(); }
879 // no assignment operator nor copy ctor
880 MemoryCriticalSectionLocker(const MemoryCriticalSectionLocker
&);
881 MemoryCriticalSectionLocker
& operator=(const MemoryCriticalSectionLocker
&);
883 wxCriticalSection
& m_critsect
;
887 static MemoryCriticalSection memLocker
;
890 // TODO: store whether this is a vector or not.
891 void * wxDebugAlloc(size_t size
, wxChar
* fileName
, int lineNum
, bool isObject
, bool WXUNUSED(isVect
) )
893 #if USE_THREADSAFE_MEMORY_ALLOCATION
894 MemoryCriticalSectionLocker
lock(memLocker
);
897 // If not in debugging allocation mode, do the normal thing
898 // so we don't leave any trace of ourselves in the node list.
900 #if defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 )
901 // VA 3.0 still has trouble in here
902 return (void *)malloc(size
);
904 if (!wxDebugContext::GetDebugMode())
906 return (void *)malloc(size
);
909 int totSize
= wxDebugContext::TotSize (size
);
910 char * buf
= (char *) malloc(totSize
);
912 wxLogMessage(wxT("Call to malloc (%ld) failed."), (long)size
);
915 wxMemStruct
* st
= (wxMemStruct
*)buf
;
916 st
->m_firstMarker
= MemStartCheck
;
917 st
->m_reqSize
= size
;
918 st
->m_fileName
= fileName
;
919 st
->m_lineNum
= lineNum
;
920 st
->m_id
= MemStructId
;
923 st
->m_isObject
= isObject
;
925 // Errors from Append() shouldn't really happen - but just in case!
926 if (st
->Append () == 0) {
927 st
->ErrorMsg ("Trying to append new node");
930 if (wxDebugContext::GetCheckPrevious ()) {
931 if (st
->CheckAllPrevious () < 0) {
932 st
->ErrorMsg ("Checking previous nodes");
936 // Set up the extra markers at the middle and end.
937 char * ptr
= wxDebugContext::MidMarkerPos (buf
);
938 * (wxMarkerType
*) ptr
= MemMidCheck
;
939 ptr
= wxDebugContext::EndMarkerPos (buf
, size
);
940 * (wxMarkerType
*) ptr
= MemEndCheck
;
942 // pointer returned points to the start of the caller's
944 void *m_actualData
= (void *) wxDebugContext::CallerMemPos (buf
);
945 st
->m_actualData
= m_actualData
;
950 // TODO: check whether was allocated as a vector
951 void wxDebugFree(void * buf
, bool WXUNUSED(isVect
) )
953 #if USE_THREADSAFE_MEMORY_ALLOCATION
954 MemoryCriticalSectionLocker
lock(memLocker
);
960 #if defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 )
961 // VA 3.0 still has trouble in here
964 // If not in debugging allocation mode, do the normal thing
965 // so we don't leave any trace of ourselves in the node list.
966 if (!wxDebugContext::GetDebugMode())
972 // Points to the start of the entire allocated area.
973 char * startPointer
= wxDebugContext::StartPos ((char *) buf
);
974 // Find the struct and make sure that it's identifiable.
975 wxMemStruct
* st
= (wxMemStruct
*) wxDebugContext::StructPos (startPointer
);
977 if (! st
->ValidateNode ())
980 // If this is the current checkpoint, we need to
981 // move the checkpoint back so it points to a valid
983 if (st
== wxDebugContext::checkPoint
)
984 wxDebugContext::checkPoint
= wxDebugContext::checkPoint
->m_prev
;
988 st
->ErrorMsg ("Unlinking deleted node");
991 // Now put in the fill char into the id slot and the caller requested
994 (void) memset (wxDebugContext::CallerMemPos (startPointer
), MemFillChar
,
1000 // Trace: send output to the current debugging stream
1001 void wxTrace(const wxChar
* ...)
1004 wxFAIL_MSG(wxT("wxTrace is now obsolete. Please use wxDebugXXX instead."));
1007 static wxChar buffer
[512];
1012 wvsprintf(buffer
,fmt
,ap
) ;
1014 vsprintf(buffer
,fmt
,ap
) ;
1019 if (wxDebugContext::HasStream())
1021 wxDebugContext::GetStream() << buffer
;
1022 wxDebugContext::GetStream().flush();
1027 OutputDebugString((LPCTSTR
)buffer
) ;
1029 OutputDebugString((const char*) buffer
) ;
1032 fprintf(stderr
, buffer
);
1038 void wxTraceLevel(int, const wxChar
* ...)
1041 wxFAIL_MSG(wxT("wxTrace is now obsolete. Please use wxDebugXXX instead."));
1043 if (wxDebugContext::GetLevel() < level
)
1047 static wxChar buffer
[512];
1052 wxWvsprintf(buffer
,fmt
,ap
) ;
1054 vsprintf(buffer
,fmt
,ap
) ;
1059 if (wxDebugContext::HasStream())
1061 wxDebugContext::GetStream() << buffer
;
1062 wxDebugContext::GetStream().flush();
1067 OutputDebugString((LPCTSTR
)buffer
) ;
1069 OutputDebugString((const char*) buffer
) ;
1072 fprintf(stderr
, buffer
);
1077 #else // wxUSE_MEMORY_TRACING && defined(__WXDEBUG__)
1078 void wxTrace(const char *WXUNUSED(fmt
) ...)
1082 void wxTraceLevel(int WXUNUSED(level
), const char *WXUNUSED(fmt
) ...)