]>
git.saurik.com Git - wxWidgets.git/blob - src/common/memory.cpp
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 (WXDEBUG && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
30 // #pragma implementation
51 #if !defined(__WATCOMC__) && !defined(__VMS__)
71 #include "wx/memory.h"
77 // wxDebugContext wxTheDebugContext;
79 Redefine new and delete so that we can pick up situations where:
80 - we overwrite or underwrite areas of malloc'd memory.
81 - we use uninitialise variables
82 Only do this in debug mode.
84 We change new to get enough memory to allocate a struct, followed
85 by the caller's requested memory, followed by a tag. The struct
86 is used to create a doubly linked list of these areas and also
87 contains another tag. The tags are used to determine when the area
88 has been over/under written.
93 Values which are used to set the markers which will be tested for
94 under/over write. There are 3 of these, one in the struct, one
95 immediately after the struct but before the caller requested memory and
96 one immediately after the requested memory.
98 #define MemStartCheck 0x23A8
99 #define MemMidCheck 0xA328
100 #define MemEndCheck 0x8A32
101 #define MemFillChar 0xAF
102 #define MemStructId 0x666D
105 External interface for the wxMemStruct class. Others are
106 defined inline within the class def. Here we only need to be able
107 to add and delete nodes from the list and handle errors in some way.
111 Used for internal "this shouldn't happen" type of errors.
113 void wxMemStruct::ErrorMsg (const char * mesg
)
115 wxTrace("wxWindows memory checking error: %s\n", mesg
);
118 // << m_fileName << ' ' << m_lineNum << endl;
122 Used when we find an overwrite or an underwrite error.
124 void wxMemStruct::ErrorMsg ()
126 wxTrace("wxWindows over/underwrite memory error: \n");
129 // cerr << m_fileName << ' ' << m_lineNum << endl;
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 if (info && info->GetClassName())
333 wxTrace("%s", info->GetClassName());
338 wxTrace(" (%s %d)", m_fileName, (int)m_lineNum);
340 wxTrace(" at $%lX, size %d\n", (long)GetActualData(), (int)RequestSize());
342 // Let's put this in standard form so IDEs can load the file at the appropriate
345 wxTrace("%s(%d): ", m_fileName
, (int)m_lineNum
);
347 if (info
&& info
->GetClassName())
348 wxTrace("%s", info
->GetClassName());
352 wxTrace(" at $%lX, size %d\n", (long)GetActualData(), (int)RequestSize());
357 wxTrace("%s(%d): ", m_fileName
, (int)m_lineNum
);
358 wxTrace("non-object data");
359 wxTrace(" at $%lX, size %d\n", (long)GetActualData(), (int)RequestSize());
363 void wxMemStruct::Dump ()
365 if (!ValidateNode()) return;
369 wxObject
*obj
= (wxObject
*)m_actualData
;
372 wxTrace("%s(%d): ", m_fileName
, (int)m_lineNum
);
374 obj
->Dump(wxDebugContext::GetStream());
375 wxTrace(" at $%lX, size %d", (long)GetActualData(), (int)RequestSize());
381 wxTrace("%s(%d): ", m_fileName
, (int)m_lineNum
);
382 wxTrace("non-object data");
383 wxTrace(" at $%lX, size %d\n", (long)GetActualData(), (int)RequestSize());
389 Validate a node. Check to see that the node is "clean" in the sense
390 that nothing has over/underwritten it etc.
392 int wxMemStruct::ValidateNode ()
394 char * startPointer
= (char *) this;
397 ErrorMsg ("Object already deleted");
399 // Can't use the error routines as we have no recognisable object.
401 wxTrace("Can't verify memory struct - all bets are off!\n");
409 for (i = 0; i < wxDebugContext::TotSize (requestSize ()); i++)
410 cout << startPointer [i];
413 if (Marker () != MemStartCheck
)
415 if (* (wxMarkerType
*) wxDebugContext::MidMarkerPos (startPointer
) != MemMidCheck
)
417 if (* (wxMarkerType
*) wxDebugContext::EndMarkerPos (startPointer
,
422 // Back to before the extra buffer and check that
423 // we can still read what we originally wrote.
424 if (Marker () != MemStartCheck
||
425 * (wxMarkerType
*) wxDebugContext::MidMarkerPos (startPointer
)
427 * (wxMarkerType
*) wxDebugContext::EndMarkerPos (startPointer
,
428 RequestSize ()) != MemEndCheck
)
438 The wxDebugContext class.
441 wxMemStruct
*wxDebugContext::m_head
= NULL
;
442 wxMemStruct
*wxDebugContext::m_tail
= NULL
;
443 // ostream *wxDebugContext::m_debugStream = NULL;
444 // streambuf *wxDebugContext::m_streamBuf = NULL;
446 // Must initialise these in wxEntry, and then delete them just before wxEntry exits
447 streambuf
*wxDebugContext::m_streamBuf
= NULL
;
448 ostream
*wxDebugContext::m_debugStream
= NULL
;
450 bool wxDebugContext::m_checkPrevious
= FALSE
;
451 int wxDebugContext::debugLevel
= 1;
452 bool wxDebugContext::debugOn
= TRUE
;
453 wxMemStruct
*wxDebugContext::checkPoint
= NULL
;
455 wxDebugContext::wxDebugContext(void)
457 // m_streamBuf = new wxDebugStreamBuf;
458 // m_debugStream = new ostream(m_streamBuf);
461 wxDebugContext::~wxDebugContext(void)
463 SetStream(NULL
, NULL
);
467 * It's bizarre, but with BC++ 4.5, the value of str changes
468 * between SetFile and SetStream.
471 void wxDebugContext::SetStream(ostream
*str
, streambuf
*buf
)
477 sprintf(buff, "SetStream (1): str is %ld", (long) str);
478 MessageBox(NULL, buff, "Memory", MB_OK);
484 m_debugStream
->flush();
485 delete m_debugStream
;
487 m_debugStream
= NULL
;
489 // Not allowed in Watcom (~streambuf is protected).
490 // Is this trying to say something significant to us??
494 streambuf
* oldBuf
= m_streamBuf
;
503 bool wxDebugContext::SetFile(const wxString
& file
)
505 ofstream
*str
= new ofstream((char *) (const char *)file
);
516 sprintf(buf, "SetFile: str is %ld", (long) str);
517 MessageBox(NULL, buf, "Memory", MB_OK);
524 bool wxDebugContext::SetStandardError(void)
526 #if !defined(_WINDLL)
527 wxDebugStreamBuf
*buf
= new wxDebugStreamBuf
;
528 ostream
*stream
= new ostream(m_streamBuf
);
529 SetStream(stream
, buf
);
538 Work out the positions of the markers by creating an array of 2 markers
539 and comparing the addresses of the 2 elements. Use this number as the
540 alignment for markers.
542 size_t wxDebugContext::CalcAlignment ()
545 return (char *) &ar
[1] - (char *) &ar
[0];
549 char * wxDebugContext::StructPos (const char * buf
)
554 char * wxDebugContext::MidMarkerPos (const char * buf
)
556 return StructPos (buf
) + PaddedSize (sizeof (wxMemStruct
));
559 char * wxDebugContext::CallerMemPos (const char * buf
)
561 return MidMarkerPos (buf
) + PaddedSize (sizeof(wxMarkerType
));
565 char * wxDebugContext::EndMarkerPos (const char * buf
, const size_t size
)
567 return CallerMemPos (buf
) + PaddedSize (size
);
572 Slightly different as this takes a pointer to the start of the caller
573 requested region and returns a pointer to the start of the buffer.
575 char * wxDebugContext::StartPos (const char * caller
)
577 return ((char *) (caller
- wxDebugContext::PaddedSize (sizeof(wxMarkerType
)) -
578 wxDebugContext::PaddedSize (sizeof (wxMemStruct
))));
582 We may need padding between various parts of the allocated memory.
583 Given a size of memory, this returns the amount of memory which should
584 be allocated in order to allow for alignment of the following object.
586 I don't know how portable this stuff is, but it seems to work for me at
587 the moment. It would be real nice if I knew more about this!
589 size_t wxDebugContext::GetPadding (const size_t size
)
591 size_t pad
= size
% CalcAlignment ();
592 return (pad
) ? sizeof(wxMarkerType
) - pad
: 0;
597 size_t wxDebugContext::PaddedSize (const size_t size
)
599 return size
+ GetPadding (size
);
603 Returns the total amount of memory which we need to get from the system
604 in order to satisfy a caller request. This includes space for the struct
605 plus markers and the caller's memory as well.
607 size_t wxDebugContext::TotSize (const size_t reqSize
)
609 return (PaddedSize (sizeof (wxMemStruct
)) + PaddedSize (reqSize
) +
610 2 * sizeof(wxMarkerType
));
615 Traverse the list of nodes executing the given function on each node.
617 void wxDebugContext::TraverseList (PmSFV func
, wxMemStruct
*from
)
620 from
= wxDebugContext::GetHead ();
622 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
624 void* data
= st
->GetActualData();
625 if ((data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
636 bool wxDebugContext::PrintList (void)
642 TraverseList ((PmSFV
)&wxMemStruct::PrintNode
, (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
));
650 bool wxDebugContext::Dump(void)
658 char* appName
= "application";
659 wxString
appNameStr("");
662 appNameStr
= wxTheApp
->GetAppName();
663 appName
= (char*) (const char*) appNameStr
;
664 wxTrace("----- Memory dump of %s at %s -----\n", appName
, WXSTRINGCAST
wxNow() );
668 wxTrace( "----- Memory dump -----\n" );
671 TraverseList ((PmSFV
)&wxMemStruct::Dump
, (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
));
681 struct wxDebugStatsStruct
686 wxDebugStatsStruct
*next
;
689 static wxDebugStatsStruct
*FindStatsStruct(wxDebugStatsStruct
*st
, char *name
)
693 if (strcmp(st
->instanceClass
, name
) == 0)
700 static wxDebugStatsStruct
*InsertStatsStruct(wxDebugStatsStruct
*head
, wxDebugStatsStruct
*st
)
706 bool wxDebugContext::PrintStatistics(bool detailed
)
714 char* appName
= "application";
715 wxString
appNameStr("");
718 appNameStr
= wxTheApp
->GetAppName();
719 appName
= (char*) (const char*) appNameStr
;
720 wxTrace("----- Memory statistics of %s at %s -----\n", appName
, WXSTRINGCAST
wxNow() );
724 wxTrace( "----- Memory statistics -----\n" );
728 bool currentMode
= GetDebugMode();
731 long noNonObjectNodes
= 0;
732 long noObjectNodes
= 0;
735 wxDebugStatsStruct
*list
= NULL
;
737 wxMemStruct
*from
= (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
);
739 from
= wxDebugContext::GetHead ();
742 for (st
= from
; st
!= 0; st
= st
->m_next
)
744 void* data
= st
->GetActualData();
745 if (detailed
&& (data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
747 char *className
= "nonobject";
748 if (st
->m_isObject
&& st
->GetActualData())
750 wxObject
*obj
= (wxObject
*)st
->GetActualData();
751 if (obj
->GetClassInfo()->GetClassName())
752 className
= obj
->GetClassInfo()->GetClassName();
754 wxDebugStatsStruct
*stats
= FindStatsStruct(list
, className
);
757 stats
= (wxDebugStatsStruct
*)malloc(sizeof(wxDebugStatsStruct
));
758 stats
->instanceClass
= className
;
759 stats
->instanceCount
= 0;
760 stats
->totalSize
= 0;
761 list
= InsertStatsStruct(list
, stats
);
763 stats
->instanceCount
++;
764 stats
->totalSize
+= st
->RequestSize();
767 if ((data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
769 totalSize
+= st
->RequestSize();
781 wxTrace("%ld objects of class %s, total size %ld\n",
782 list
->instanceCount
, list
->instanceClass
, list
->totalSize
);
783 wxDebugStatsStruct
*old
= list
;
790 SetDebugMode(currentMode
);
792 wxTrace("Number of object items: %ld\n", noObjectNodes
);
793 wxTrace("Number of non-object items: %ld\n", noNonObjectNodes
);
794 wxTrace("Total allocated size: %ld\n", totalSize
);
803 bool wxDebugContext::PrintClasses(void)
810 char* appName
= "application";
811 wxString
appNameStr("");
814 appNameStr
= wxTheApp
->GetAppName();
815 appName
= (char*) (const char*) appNameStr
;
816 wxTrace("----- Classes in %s -----\n", appName
);
824 wxClassInfo::sm_classTable
->BeginFind();
825 node
= wxClassInfo::sm_classTable
->Next();
828 info
= (wxClassInfo
*)node
->Data();
829 if (info
->GetClassName())
831 wxTrace("%s ", info
->GetClassName());
833 if (info
->GetBaseClassName1() && !info
->GetBaseClassName2())
834 wxTrace("is a %s", info
->GetBaseClassName1());
835 else if (info
->GetBaseClassName1() && info
->GetBaseClassName2())
836 wxTrace("is a %s, %s", info
->GetBaseClassName1(), info
->GetBaseClassName2());
837 if (info
->GetConstructor())
838 wxTrace(": dynamic\n");
842 node
= wxClassInfo::sm_classTable
->Next();
845 wxTrace("\nThere are %d classes derived from wxObject.\n\n\n", n
);
849 void wxDebugContext::SetCheckpoint(bool all
)
857 // Checks all nodes since checkpoint, or since start.
858 int wxDebugContext::Check(bool checkAll
)
862 wxMemStruct
*from
= (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
);
863 if (!from
|| checkAll
)
864 from
= wxDebugContext::GetHead ();
866 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
869 nFailures
+= st
->CheckBlock ();
877 // Count the number of non-wxDebugContext-related objects
878 // that are outstanding
879 int wxDebugContext::CountObjectsLeft(void)
883 wxMemStruct
*from
= wxDebugContext::GetHead ();
885 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
887 void* data
= st
->GetActualData();
888 if ((data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
896 The global operator new used for everything apart from getting
897 dynamic storage within this function itself.
900 // We'll only do malloc and free for the moment: leave the interesting
901 // stuff for the wxObject versions.
903 #if WXDEBUG && wxUSE_GLOBAL_MEMORY_OPERATORS
909 // Seems OK all of a sudden. Maybe to do with linking with multithreaded library?
910 #if 0 // def _MSC_VER
911 #define NO_DEBUG_ALLOCATION
914 // Unfortunately ~wxDebugStreamBuf doesn't work (VC++ 5) when we enable the debugging
915 // code. I have no idea why. In BC++ 4.5, we have a similar problem the debug
916 // stream myseriously changing pointer address between being passed from SetFile to SetStream.
917 // See docs/msw/issues.txt.
918 void * operator new (size_t size
, char * fileName
, int lineNum
)
920 #ifdef NO_DEBUG_ALLOCATION
923 return wxDebugAlloc(size
, fileName
, lineNum
, FALSE
, FALSE
);
927 #if !( defined (_MSC_VER) && (_MSC_VER <= 1020) )
928 void * operator new[] (size_t size
, char * fileName
, int lineNum
)
930 #ifdef NO_DEBUG_ALLOCATION
933 return wxDebugAlloc(size
, fileName
, lineNum
, FALSE
, TRUE
);
938 void operator delete (void * buf
)
940 #ifdef NO_DEBUG_ALLOCATION
949 void operator delete(void* pData
, char* /* fileName */, int /* lineNum */)
951 ::operator delete(pData
);
955 #if !( defined (_MSC_VER) && (_MSC_VER <= 1020) )
956 void operator delete[] (void * buf
)
958 #ifdef NO_DEBUG_ALLOCATION
961 wxDebugFree(buf
, TRUE
);
968 // TODO: store whether this is a vector or not.
969 void * wxDebugAlloc(size_t size
, char * fileName
, int lineNum
, bool isObject
, bool WXUNUSED(isVect
) )
971 // If not in debugging allocation mode, do the normal thing
972 // so we don't leave any trace of ourselves in the node list.
974 if (!wxDebugContext::GetDebugMode())
976 return (void *)malloc(size
);
979 char * buf
= (char *) malloc(wxDebugContext::TotSize (size
));
981 wxTrace("Call to malloc (%ld) failed.\n", (long)size
);
984 wxMemStruct
* st
= (wxMemStruct
*)buf
;
985 st
->m_firstMarker
= MemStartCheck
;
986 st
->m_reqSize
= size
;
987 st
->m_fileName
= fileName
;
988 st
->m_lineNum
= lineNum
;
989 st
->m_id
= MemStructId
;
992 st
->m_isObject
= isObject
;
994 // Errors from Append() shouldn't really happen - but just in case!
995 if (st
->Append () == 0) {
996 st
->ErrorMsg ("Trying to append new node");
999 if (wxDebugContext::GetCheckPrevious ()) {
1000 if (st
->CheckAllPrevious () < 0) {
1001 st
->ErrorMsg ("Checking previous nodes");
1005 // Set up the extra markers at the middle and end.
1006 char * ptr
= wxDebugContext::MidMarkerPos (buf
);
1007 * (wxMarkerType
*) ptr
= MemMidCheck
;
1008 ptr
= wxDebugContext::EndMarkerPos (buf
, size
);
1009 * (wxMarkerType
*) ptr
= MemEndCheck
;
1011 // pointer returned points to the start of the caller's
1013 void *m_actualData
= (void *) wxDebugContext::CallerMemPos (buf
);
1014 st
->m_actualData
= m_actualData
;
1016 return m_actualData
;
1019 // TODO: check whether was allocated as a vector
1020 void wxDebugFree(void * buf
, bool WXUNUSED(isVect
) )
1025 // If not in debugging allocation mode, do the normal thing
1026 // so we don't leave any trace of ourselves in the node list.
1027 if (!wxDebugContext::GetDebugMode())
1033 // Points to the start of the entire allocated area.
1034 char * startPointer
= wxDebugContext::StartPos ((char *) buf
);
1035 // Find the struct and make sure that it's identifiable.
1036 wxMemStruct
* st
= (wxMemStruct
*) wxDebugContext::StructPos (startPointer
);
1038 if (! st
->ValidateNode ())
1041 // If this is the current checkpoint, we need to
1042 // move the checkpoint back so it points to a valid
1044 if (st
== wxDebugContext::checkPoint
)
1045 wxDebugContext::checkPoint
= wxDebugContext::checkPoint
->m_prev
;
1047 if (! st
->Unlink ())
1049 st
->ErrorMsg ("Unlinking deleted node");
1052 // Now put in the fill char into the id slot and the caller requested
1053 // memory locations.
1055 (void) memset (wxDebugContext::CallerMemPos (startPointer
), MemFillChar
,
1056 st
->RequestSize ());
1058 // Don't allow delayed freeing of memory in this version
1059 // if (!wxDebugContext::GetDelayFree())
1060 // free((void *)st);
1064 // Trace: send output to the current debugging stream
1065 void wxTrace(const char *fmt
...)
1068 static char buffer
[512];
1073 wvsprintf(buffer
,fmt
,ap
) ;
1075 vsprintf(buffer
,fmt
,ap
) ;
1080 if (wxDebugContext::HasStream())
1082 wxDebugContext::GetStream() << buffer
;
1083 wxDebugContext::GetStream().flush();
1087 OutputDebugString((LPCSTR
)buffer
) ;
1089 fprintf(stderr
, buffer
);
1094 void wxTraceLevel(int level
, const char *fmt
...)
1096 if (wxDebugContext::GetLevel() < level
)
1100 static char buffer
[512];
1105 wvsprintf(buffer
,fmt
,ap
) ;
1107 vsprintf(buffer
,fmt
,ap
) ;
1112 if (wxDebugContext::HasStream())
1114 wxDebugContext::GetStream() << buffer
;
1115 wxDebugContext::GetStream().flush();
1119 OutputDebugString((LPCSTR
)buffer
) ;
1121 fprintf(stderr
, buffer
);
1125 #else // wxUSE_MEMORY_TRACING && WXDEBUG
1126 void wxTrace(const char *WXUNUSED(fmt
) ...)
1130 void wxTraceLevel(int WXUNUSED(level
), const char *WXUNUSED(fmt
) ...)