]>
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
47 #if !defined(__WATCOMC__) && !defined(__VMS__)
67 #include "wx/memory.h"
73 // wxDebugContext wxTheDebugContext;
75 Redefine new and delete so that we can pick up situations where:
76 - we overwrite or underwrite areas of malloc'd memory.
77 - we use uninitialise variables
78 Only do this in debug mode.
80 We change new to get enough memory to allocate a struct, followed
81 by the caller's requested memory, followed by a tag. The struct
82 is used to create a doubly linked list of these areas and also
83 contains another tag. The tags are used to determine when the area
84 has been over/under written.
89 Values which are used to set the markers which will be tested for
90 under/over write. There are 3 of these, one in the struct, one
91 immediately after the struct but before the caller requested memory and
92 one immediately after the requested memory.
94 #define MemStartCheck 0x23A8
95 #define MemMidCheck 0xA328
96 #define MemEndCheck 0x8A32
97 #define MemFillChar 0xAF
98 #define MemStructId 0x666D
101 External interface for the wxMemStruct class. Others are
102 defined inline within the class def. Here we only need to be able
103 to add and delete nodes from the list and handle errors in some way.
107 Used for internal "this shouldn't happen" type of errors.
109 void wxMemStruct::ErrorMsg (const char * mesg
)
111 wxTrace("wxWindows memory checking error: %s\n", mesg
);
114 // << m_fileName << ' ' << m_lineNum << endl;
118 Used when we find an overwrite or an underwrite error.
120 void wxMemStruct::ErrorMsg ()
122 wxTrace("wxWindows over/underwrite memory error: \n");
125 // cerr << m_fileName << ' ' << m_lineNum << endl;
130 We want to find out if pointers have been overwritten as soon as is
131 possible, so test everything before we dereference it. Of course it's still
132 quite possible that, if things have been overwritten, this function will
133 fall over, but the only way of dealing with that would cost too much in terms
136 int wxMemStruct::AssertList ()
138 if (wxDebugContext::GetHead () != 0 && ! (wxDebugContext::GetHead ())->AssertIt () ||
139 wxDebugContext::GetTail () != 0 && ! wxDebugContext::GetTail ()->AssertIt ()) {
140 ErrorMsg ("Head or tail pointers trashed");
148 Check that the thing we're pointing to has the correct id for a wxMemStruct
149 object and also that it's previous and next pointers are pointing at objects
150 which have valid ids.
151 This is definitely not perfect since we could fall over just trying to access
152 any of the slots which we use here, but I think it's about the best that I
153 can do without doing something like taking all new wxMemStruct pointers and
154 comparing them against all known pointer within the list and then only
155 doing this sort of check _after_ you've found the pointer in the list. That
156 would be safer, but also much more time consuming.
158 int wxMemStruct::AssertIt ()
160 return (m_id
== MemStructId
&&
161 (m_prev
== 0 || m_prev
->m_id
== MemStructId
) &&
162 (m_next
== 0 || m_next
->m_id
== MemStructId
));
167 Additions are always at the tail of the list.
168 Returns 0 on error, non-zero on success.
170 int wxMemStruct::Append ()
175 if (wxDebugContext::GetHead () == 0) {
176 if (wxDebugContext::GetTail () != 0) {
177 ErrorMsg ("Null list should have a null tail pointer");
180 (void) wxDebugContext::SetHead (this);
181 (void) wxDebugContext::SetTail (this);
183 wxDebugContext::GetTail ()->m_next
= this;
184 this->m_prev
= wxDebugContext::GetTail ();
185 (void) wxDebugContext::SetTail (this);
192 Don't actually free up anything here as the space which is used
193 by the node will be free'd up when the whole block is free'd.
194 Returns 0 on error, non-zero on success.
196 int wxMemStruct::Unlink ()
201 if (wxDebugContext::GetHead () == 0 || wxDebugContext::GetTail () == 0) {
202 ErrorMsg ("Trying to remove node from empty list");
206 // Handle the part of the list before this node.
208 if (this != wxDebugContext::GetHead ()) {
209 ErrorMsg ("No previous node for non-head node");
212 (void) wxDebugContext::SetHead (m_next
);
214 if (! m_prev
->AssertIt ()) {
215 ErrorMsg ("Trashed previous pointer");
219 if (m_prev
->m_next
!= this) {
220 ErrorMsg ("List is inconsistent");
223 m_prev
->m_next
= m_next
;
226 // Handle the part of the list after this node.
228 if (this != wxDebugContext::GetTail ()) {
229 ErrorMsg ("No next node for non-tail node");
232 (void) wxDebugContext::SetTail (m_prev
);
234 if (! m_next
->AssertIt ()) {
235 ErrorMsg ("Trashed next pointer");
239 if (m_next
->m_prev
!= this) {
240 ErrorMsg ("List is inconsistent");
243 m_next
->m_prev
= m_prev
;
252 Checks a node and block of memory to see that the markers are still
255 int wxMemStruct::CheckBlock ()
259 if (m_firstMarker
!= MemStartCheck
) {
264 char * pointer
= wxDebugContext::MidMarkerPos ((char *) this);
265 if (* (wxMarkerType
*) pointer
!= MemMidCheck
) {
270 pointer
= wxDebugContext::EndMarkerPos ((char *) this, RequestSize ());
271 if (* (wxMarkerType
*) pointer
!= MemEndCheck
) {
281 Check the list of nodes to see if they are all ok.
283 int wxMemStruct::CheckAllPrevious ()
287 for (wxMemStruct
* st
= this->m_prev
; st
!= 0; st
= st
->m_prev
) {
289 nFailures
+= st
->CheckBlock ();
299 When we delete a node we set the id slot to a specific value and then test
300 against this to see if a nodes have been deleted previously. I don't
301 just set the entire memory to the fillChar because then I'd be overwriting
302 useful stuff like the vtbl which may be needed to output the error message
303 including the file name and line numbers. Without this info the whole point
304 of this class is lost!
306 void wxMemStruct::SetDeleted ()
311 int wxMemStruct::IsDeleted ()
313 return (m_id
== MemFillChar
);
318 Print out a single node. There are many far better ways of doing this
319 but this will suffice for now.
321 void wxMemStruct::PrintNode ()
325 wxObject
*obj
= (wxObject
*)m_actualData
;
326 wxClassInfo
*info
= obj
->GetClassInfo();
328 if (info && info->GetClassName())
329 wxTrace("%s", info->GetClassName());
334 wxTrace(" (%s %d)", m_fileName, (int)m_lineNum);
336 wxTrace(" at $%lX, size %d\n", (long)GetActualData(), (int)RequestSize());
338 // Let's put this in standard form so IDEs can load the file at the appropriate
341 wxTrace("%s(%d): ", m_fileName
, (int)m_lineNum
);
343 if (info
&& info
->GetClassName())
344 wxTrace("%s", info
->GetClassName());
348 wxTrace(" at $%lX, size %d\n", (long)GetActualData(), (int)RequestSize());
353 wxTrace("%s(%d): ", m_fileName
, (int)m_lineNum
);
354 wxTrace("non-object data");
355 wxTrace(" at $%lX, size %d\n", (long)GetActualData(), (int)RequestSize());
359 void wxMemStruct::Dump ()
361 if (!ValidateNode()) return;
365 wxObject
*obj
= (wxObject
*)m_actualData
;
368 wxTrace("%s(%d): ", m_fileName
, (int)m_lineNum
);
370 obj
->Dump(wxDebugContext::GetStream());
371 wxTrace(" at $%lX, size %d", (long)GetActualData(), (int)RequestSize());
377 wxTrace("%s(%d): ", m_fileName
, (int)m_lineNum
);
378 wxTrace("non-object data");
379 wxTrace(" at $%lX, size %d\n", (long)GetActualData(), (int)RequestSize());
385 Validate a node. Check to see that the node is "clean" in the sense
386 that nothing has over/underwritten it etc.
388 int wxMemStruct::ValidateNode ()
390 char * startPointer
= (char *) this;
393 ErrorMsg ("Object already deleted");
395 // Can't use the error routines as we have no recognisable object.
397 wxTrace("Can't verify memory struct - all bets are off!\n");
405 for (i = 0; i < wxDebugContext::TotSize (requestSize ()); i++)
406 cout << startPointer [i];
409 if (Marker () != MemStartCheck
)
411 if (* (wxMarkerType
*) wxDebugContext::MidMarkerPos (startPointer
) != MemMidCheck
)
413 if (* (wxMarkerType
*) wxDebugContext::EndMarkerPos (startPointer
,
418 // Back to before the extra buffer and check that
419 // we can still read what we originally wrote.
420 if (Marker () != MemStartCheck
||
421 * (wxMarkerType
*) wxDebugContext::MidMarkerPos (startPointer
)
423 * (wxMarkerType
*) wxDebugContext::EndMarkerPos (startPointer
,
424 RequestSize ()) != MemEndCheck
)
434 The wxDebugContext class.
437 wxMemStruct
*wxDebugContext::m_head
= NULL
;
438 wxMemStruct
*wxDebugContext::m_tail
= NULL
;
439 // ostream *wxDebugContext::m_debugStream = NULL;
440 // streambuf *wxDebugContext::m_streamBuf = NULL;
442 // Must initialise these in wxEntry, and then delete them just before wxEntry exits
443 streambuf
*wxDebugContext::m_streamBuf
= NULL
;
444 ostream
*wxDebugContext::m_debugStream
= NULL
;
446 bool wxDebugContext::m_checkPrevious
= FALSE
;
447 int wxDebugContext::debugLevel
= 1;
448 bool wxDebugContext::debugOn
= TRUE
;
449 wxMemStruct
*wxDebugContext::checkPoint
= NULL
;
451 wxDebugContext::wxDebugContext(void)
453 // m_streamBuf = new wxDebugStreamBuf;
454 // m_debugStream = new ostream(m_streamBuf);
457 wxDebugContext::~wxDebugContext(void)
459 SetStream(NULL
, NULL
);
463 * It's bizarre, but with BC++ 4.5, the value of str changes
464 * between SetFile and SetStream.
467 void wxDebugContext::SetStream(ostream
*str
, streambuf
*buf
)
473 sprintf(buff, "SetStream (1): str is %ld", (long) str);
474 MessageBox(NULL, buff, "Memory", MB_OK);
480 m_debugStream
->flush();
481 delete m_debugStream
;
483 m_debugStream
= NULL
;
485 // Not allowed in Watcom (~streambuf is protected).
486 // Is this trying to say something significant to us??
490 streambuf
* oldBuf
= m_streamBuf
;
499 bool wxDebugContext::SetFile(const wxString
& file
)
501 ofstream
*str
= new ofstream((char *) (const char *)file
);
512 sprintf(buf, "SetFile: str is %ld", (long) str);
513 MessageBox(NULL, buf, "Memory", MB_OK);
520 bool wxDebugContext::SetStandardError(void)
522 #if !defined(_WINDLL)
523 wxDebugStreamBuf
*buf
= new wxDebugStreamBuf
;
524 ostream
*stream
= new ostream(m_streamBuf
);
525 SetStream(stream
, buf
);
534 Work out the positions of the markers by creating an array of 2 markers
535 and comparing the addresses of the 2 elements. Use this number as the
536 alignment for markers.
538 size_t wxDebugContext::CalcAlignment ()
541 return (char *) &ar
[1] - (char *) &ar
[0];
545 char * wxDebugContext::StructPos (const char * buf
)
550 char * wxDebugContext::MidMarkerPos (const char * buf
)
552 return StructPos (buf
) + PaddedSize (sizeof (wxMemStruct
));
555 char * wxDebugContext::CallerMemPos (const char * buf
)
557 return MidMarkerPos (buf
) + PaddedSize (sizeof(wxMarkerType
));
561 char * wxDebugContext::EndMarkerPos (const char * buf
, const size_t size
)
563 return CallerMemPos (buf
) + PaddedSize (size
);
568 Slightly different as this takes a pointer to the start of the caller
569 requested region and returns a pointer to the start of the buffer.
571 char * wxDebugContext::StartPos (const char * caller
)
573 return ((char *) (caller
- wxDebugContext::PaddedSize (sizeof(wxMarkerType
)) -
574 wxDebugContext::PaddedSize (sizeof (wxMemStruct
))));
578 We may need padding between various parts of the allocated memory.
579 Given a size of memory, this returns the amount of memory which should
580 be allocated in order to allow for alignment of the following object.
582 I don't know how portable this stuff is, but it seems to work for me at
583 the moment. It would be real nice if I knew more about this!
585 size_t wxDebugContext::GetPadding (const size_t size
)
587 size_t pad
= size
% CalcAlignment ();
588 return (pad
) ? sizeof(wxMarkerType
) - pad
: 0;
593 size_t wxDebugContext::PaddedSize (const size_t size
)
595 return size
+ GetPadding (size
);
599 Returns the total amount of memory which we need to get from the system
600 in order to satisfy a caller request. This includes space for the struct
601 plus markers and the caller's memory as well.
603 size_t wxDebugContext::TotSize (const size_t reqSize
)
605 return (PaddedSize (sizeof (wxMemStruct
)) + PaddedSize (reqSize
) +
606 2 * sizeof(wxMarkerType
));
611 Traverse the list of nodes executing the given function on each node.
613 void wxDebugContext::TraverseList (PmSFV func
, wxMemStruct
*from
)
616 from
= wxDebugContext::GetHead ();
618 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
620 void* data
= st
->GetActualData();
621 if ((data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
632 bool wxDebugContext::PrintList (void)
638 TraverseList ((PmSFV
)&wxMemStruct::PrintNode
, (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
));
646 bool wxDebugContext::Dump(void)
654 char* appName
= "application";
655 wxString
appNameStr("");
658 appNameStr
= wxTheApp
->GetAppName();
659 appName
= (char*) (const char*) appNameStr
;
660 wxTrace("----- Memory dump of %s at %s -----\n", appName
, WXSTRINGCAST
wxNow() );
664 wxTrace( "----- Memory dump -----\n" );
667 TraverseList ((PmSFV
)&wxMemStruct::Dump
, (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
));
677 struct wxDebugStatsStruct
682 wxDebugStatsStruct
*next
;
685 static wxDebugStatsStruct
*FindStatsStruct(wxDebugStatsStruct
*st
, char *name
)
689 if (strcmp(st
->instanceClass
, name
) == 0)
696 static wxDebugStatsStruct
*InsertStatsStruct(wxDebugStatsStruct
*head
, wxDebugStatsStruct
*st
)
702 bool wxDebugContext::PrintStatistics(bool detailed
)
710 char* appName
= "application";
711 wxString
appNameStr("");
714 appNameStr
= wxTheApp
->GetAppName();
715 appName
= (char*) (const char*) appNameStr
;
716 wxTrace("----- Memory statistics of %s at %s -----\n", appName
, WXSTRINGCAST
wxNow() );
720 wxTrace( "----- Memory statistics -----\n" );
724 bool currentMode
= GetDebugMode();
727 long noNonObjectNodes
= 0;
728 long noObjectNodes
= 0;
731 wxDebugStatsStruct
*list
= NULL
;
733 wxMemStruct
*from
= (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
);
735 from
= wxDebugContext::GetHead ();
738 for (st
= from
; st
!= 0; st
= st
->m_next
)
740 void* data
= st
->GetActualData();
741 if (detailed
&& (data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
743 char *className
= "nonobject";
744 if (st
->m_isObject
&& st
->GetActualData())
746 wxObject
*obj
= (wxObject
*)st
->GetActualData();
747 if (obj
->GetClassInfo()->GetClassName())
748 className
= obj
->GetClassInfo()->GetClassName();
750 wxDebugStatsStruct
*stats
= FindStatsStruct(list
, className
);
753 stats
= (wxDebugStatsStruct
*)malloc(sizeof(wxDebugStatsStruct
));
754 stats
->instanceClass
= className
;
755 stats
->instanceCount
= 0;
756 stats
->totalSize
= 0;
757 list
= InsertStatsStruct(list
, stats
);
759 stats
->instanceCount
++;
760 stats
->totalSize
+= st
->RequestSize();
763 if ((data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
765 totalSize
+= st
->RequestSize();
777 wxTrace("%ld objects of class %s, total size %ld\n",
778 list
->instanceCount
, list
->instanceClass
, list
->totalSize
);
779 wxDebugStatsStruct
*old
= list
;
786 SetDebugMode(currentMode
);
788 wxTrace("Number of object items: %ld\n", noObjectNodes
);
789 wxTrace("Number of non-object items: %ld\n", noNonObjectNodes
);
790 wxTrace("Total allocated size: %ld\n", totalSize
);
799 bool wxDebugContext::PrintClasses(void)
806 char* appName
= "application";
807 wxString
appNameStr("");
810 appNameStr
= wxTheApp
->GetAppName();
811 appName
= (char*) (const char*) appNameStr
;
812 wxTrace("----- Classes in %s -----\n", appName
);
820 wxClassInfo::sm_classTable
->BeginFind();
821 node
= wxClassInfo::sm_classTable
->Next();
824 info
= (wxClassInfo
*)node
->Data();
825 if (info
->GetClassName())
827 wxTrace("%s ", info
->GetClassName());
829 if (info
->GetBaseClassName1() && !info
->GetBaseClassName2())
830 wxTrace("is a %s", info
->GetBaseClassName1());
831 else if (info
->GetBaseClassName1() && info
->GetBaseClassName2())
832 wxTrace("is a %s, %s", info
->GetBaseClassName1(), info
->GetBaseClassName2());
833 if (info
->GetConstructor())
834 wxTrace(": dynamic\n");
838 node
= wxClassInfo::sm_classTable
->Next();
841 wxTrace("\nThere are %d classes derived from wxObject.\n\n\n", n
);
845 void wxDebugContext::SetCheckpoint(bool all
)
853 // Checks all nodes since checkpoint, or since start.
854 int wxDebugContext::Check(bool checkAll
)
858 wxMemStruct
*from
= (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
);
859 if (!from
|| checkAll
)
860 from
= wxDebugContext::GetHead ();
862 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
865 nFailures
+= st
->CheckBlock ();
873 // Count the number of non-wxDebugContext-related objects
874 // that are outstanding
875 int wxDebugContext::CountObjectsLeft(void)
879 wxMemStruct
*from
= wxDebugContext::GetHead ();
881 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
883 void* data
= st
->GetActualData();
884 if ((data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
892 The global operator new used for everything apart from getting
893 dynamic storage within this function itself.
896 // We'll only do malloc and free for the moment: leave the interesting
897 // stuff for the wxObject versions.
899 #if WXDEBUG && wxUSE_GLOBAL_MEMORY_OPERATORS
905 // Seems OK all of a sudden. Maybe to do with linking with multithreaded library?
906 #if 0 // def _MSC_VER
907 #define NO_DEBUG_ALLOCATION
910 // Unfortunately ~wxDebugStreamBuf doesn't work (VC++ 5) when we enable the debugging
911 // code. I have no idea why. In BC++ 4.5, we have a similar problem the debug
912 // stream myseriously changing pointer address between being passed from SetFile to SetStream.
913 // See docs/msw/issues.txt.
914 void * operator new (size_t size
, char * fileName
, int lineNum
)
916 #ifdef NO_DEBUG_ALLOCATION
919 return wxDebugAlloc(size
, fileName
, lineNum
, FALSE
, FALSE
);
923 #if !( defined (_MSC_VER) && (_MSC_VER <= 1020) )
924 void * operator new[] (size_t size
, char * fileName
, int lineNum
)
926 #ifdef NO_DEBUG_ALLOCATION
929 return wxDebugAlloc(size
, fileName
, lineNum
, FALSE
, TRUE
);
934 void operator delete (void * buf
)
936 #ifdef NO_DEBUG_ALLOCATION
945 void operator delete(void* pData
, char* /* fileName */, int /* lineNum */)
947 ::operator delete(pData
);
951 #if !( defined (_MSC_VER) && (_MSC_VER <= 1020) )
952 void operator delete[] (void * buf
)
954 #ifdef NO_DEBUG_ALLOCATION
957 wxDebugFree(buf
, TRUE
);
964 // TODO: store whether this is a vector or not.
965 void * wxDebugAlloc(size_t size
, char * fileName
, int lineNum
, bool isObject
, bool WXUNUSED(isVect
) )
967 // If not in debugging allocation mode, do the normal thing
968 // so we don't leave any trace of ourselves in the node list.
970 if (!wxDebugContext::GetDebugMode())
972 return (void *)malloc(size
);
975 char * buf
= (char *) malloc(wxDebugContext::TotSize (size
));
977 wxTrace("Call to malloc (%ld) failed.\n", (long)size
);
980 wxMemStruct
* st
= (wxMemStruct
*)buf
;
981 st
->m_firstMarker
= MemStartCheck
;
982 st
->m_reqSize
= size
;
983 st
->m_fileName
= fileName
;
984 st
->m_lineNum
= lineNum
;
985 st
->m_id
= MemStructId
;
988 st
->m_isObject
= isObject
;
990 // Errors from Append() shouldn't really happen - but just in case!
991 if (st
->Append () == 0) {
992 st
->ErrorMsg ("Trying to append new node");
995 if (wxDebugContext::GetCheckPrevious ()) {
996 if (st
->CheckAllPrevious () < 0) {
997 st
->ErrorMsg ("Checking previous nodes");
1001 // Set up the extra markers at the middle and end.
1002 char * ptr
= wxDebugContext::MidMarkerPos (buf
);
1003 * (wxMarkerType
*) ptr
= MemMidCheck
;
1004 ptr
= wxDebugContext::EndMarkerPos (buf
, size
);
1005 * (wxMarkerType
*) ptr
= MemEndCheck
;
1007 // pointer returned points to the start of the caller's
1009 void *m_actualData
= (void *) wxDebugContext::CallerMemPos (buf
);
1010 st
->m_actualData
= m_actualData
;
1012 return m_actualData
;
1015 // TODO: check whether was allocated as a vector
1016 void wxDebugFree(void * buf
, bool WXUNUSED(isVect
) )
1021 // If not in debugging allocation mode, do the normal thing
1022 // so we don't leave any trace of ourselves in the node list.
1023 if (!wxDebugContext::GetDebugMode())
1029 // Points to the start of the entire allocated area.
1030 char * startPointer
= wxDebugContext::StartPos ((char *) buf
);
1031 // Find the struct and make sure that it's identifiable.
1032 wxMemStruct
* st
= (wxMemStruct
*) wxDebugContext::StructPos (startPointer
);
1034 if (! st
->ValidateNode ())
1037 // If this is the current checkpoint, we need to
1038 // move the checkpoint back so it points to a valid
1040 if (st
== wxDebugContext::checkPoint
)
1041 wxDebugContext::checkPoint
= wxDebugContext::checkPoint
->m_prev
;
1043 if (! st
->Unlink ())
1045 st
->ErrorMsg ("Unlinking deleted node");
1048 // Now put in the fill char into the id slot and the caller requested
1049 // memory locations.
1051 (void) memset (wxDebugContext::CallerMemPos (startPointer
), MemFillChar
,
1052 st
->RequestSize ());
1054 // Don't allow delayed freeing of memory in this version
1055 // if (!wxDebugContext::GetDelayFree())
1056 // free((void *)st);
1060 // Trace: send output to the current debugging stream
1061 void wxTrace(const char *fmt
...)
1064 static char buffer
[512];
1069 wvsprintf(buffer
,fmt
,ap
) ;
1071 vsprintf(buffer
,fmt
,ap
) ;
1076 if (wxDebugContext::HasStream())
1078 wxDebugContext::GetStream() << buffer
;
1079 wxDebugContext::GetStream().flush();
1083 OutputDebugString((LPCSTR
)buffer
) ;
1085 fprintf(stderr
, buffer
);
1090 void wxTraceLevel(int level
, const char *fmt
...)
1092 if (wxDebugContext::GetLevel() < level
)
1096 static char buffer
[512];
1101 wvsprintf(buffer
,fmt
,ap
) ;
1103 vsprintf(buffer
,fmt
,ap
) ;
1108 if (wxDebugContext::HasStream())
1110 wxDebugContext::GetStream() << buffer
;
1111 wxDebugContext::GetStream().flush();
1115 OutputDebugString((LPCSTR
)buffer
) ;
1117 fprintf(stderr
, buffer
);
1121 #else // wxUSE_MEMORY_TRACING && WXDEBUG
1122 void wxTrace(const char *WXUNUSED(fmt
) ...)
1126 void wxTraceLevel(int WXUNUSED(level
), const char *WXUNUSED(fmt
) ...)