]>
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 && USE_MEMORY_TRACING) || USE_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());
340 wxTrace("Non-object data");
342 wxTrace(" (%s %d)", m_fileName
, (int)m_lineNum
);
343 wxTrace(" at $%lX, size %d\n", (long)GetActualData(), (int)RequestSize());
347 void wxMemStruct::Dump ()
349 if (!ValidateNode()) return;
353 wxObject
*obj
= (wxObject
*)m_actualData
;
354 // wxClassInfo *info = obj->GetClassInfo();
357 wxTrace("Item (%s %d)", m_fileName
, (int)m_lineNum
);
361 wxTrace(" at $%lX, size %d: ", (long)GetActualData(), (int)RequestSize());
362 // wxTrace(info->GetClassName());
363 obj
->Dump(wxDebugContext::GetStream());
368 wxTrace("Non-object data");
370 wxTrace(" (%s %d)", m_fileName
, (int)m_lineNum
);
371 wxTrace(" at $%lX, size %d\n", (long)GetActualData(), (int)RequestSize());
377 Validate a node. Check to see that the node is "clean" in the sense
378 that nothing has over/underwritten it etc.
380 int wxMemStruct::ValidateNode ()
382 char * startPointer
= (char *) this;
385 ErrorMsg ("Object already deleted");
387 // Can't use the error routines as we have no recognisable object.
389 wxTrace("Can't verify memory struct - all bets are off!\n");
397 for (i = 0; i < wxDebugContext::TotSize (requestSize ()); i++)
398 cout << startPointer [i];
401 if (Marker () != MemStartCheck
)
403 if (* (wxMarkerType
*) wxDebugContext::MidMarkerPos (startPointer
) != MemMidCheck
)
405 if (* (wxMarkerType
*) wxDebugContext::EndMarkerPos (startPointer
,
410 // Back to before the extra buffer and check that
411 // we can still read what we originally wrote.
412 if (Marker () != MemStartCheck
||
413 * (wxMarkerType
*) wxDebugContext::MidMarkerPos (startPointer
)
415 * (wxMarkerType
*) wxDebugContext::EndMarkerPos (startPointer
,
416 RequestSize ()) != MemEndCheck
)
426 The wxDebugContext class.
429 wxMemStruct
*wxDebugContext::m_head
= NULL
;
430 wxMemStruct
*wxDebugContext::m_tail
= NULL
;
431 // ostream *wxDebugContext::m_debugStream = NULL;
432 // streambuf *wxDebugContext::m_streamBuf = NULL;
434 // Must initialise these in wxEntry, and then delete them just before wxEntry exits
435 streambuf
*wxDebugContext::m_streamBuf
= NULL
;
436 ostream
*wxDebugContext::m_debugStream
= NULL
;
438 bool wxDebugContext::m_checkPrevious
= FALSE
;
439 int wxDebugContext::debugLevel
= 1;
440 bool wxDebugContext::debugOn
= TRUE
;
441 wxMemStruct
*wxDebugContext::checkPoint
= NULL
;
443 wxDebugContext::wxDebugContext(void)
445 // m_streamBuf = new wxDebugStreamBuf;
446 // m_debugStream = new ostream(m_streamBuf);
449 wxDebugContext::~wxDebugContext(void)
451 SetStream(NULL
, NULL
);
455 * It's bizarre, but with BC++ 4.5, the value of str changes
456 * between SetFile and SetStream.
459 void wxDebugContext::SetStream(ostream
*str
, streambuf
*buf
)
465 sprintf(buff, "SetStream (1): str is %ld", (long) str);
466 MessageBox(NULL, buff, "Memory", MB_OK);
472 m_debugStream
->flush();
473 delete m_debugStream
;
475 m_debugStream
= NULL
;
477 // Not allowed in Watcom (~streambuf is protected).
478 // Is this trying to say something significant to us??
482 streambuf
* oldBuf
= m_streamBuf
;
491 bool wxDebugContext::SetFile(const wxString
& file
)
493 ofstream
*str
= new ofstream((char *) (const char *)file
);
504 sprintf(buf, "SetFile: str is %ld", (long) str);
505 MessageBox(NULL, buf, "Memory", MB_OK);
512 bool wxDebugContext::SetStandardError(void)
514 #if !defined(_WINDLL)
515 wxDebugStreamBuf
*buf
= new wxDebugStreamBuf
;
516 ostream
*stream
= new ostream(m_streamBuf
);
517 SetStream(stream
, buf
);
526 Work out the positions of the markers by creating an array of 2 markers
527 and comparing the addresses of the 2 elements. Use this number as the
528 alignment for markers.
530 size_t wxDebugContext::CalcAlignment ()
533 return (char *) &ar
[1] - (char *) &ar
[0];
537 char * wxDebugContext::StructPos (const char * buf
)
542 char * wxDebugContext::MidMarkerPos (const char * buf
)
544 return StructPos (buf
) + PaddedSize (sizeof (wxMemStruct
));
547 char * wxDebugContext::CallerMemPos (const char * buf
)
549 return MidMarkerPos (buf
) + PaddedSize (sizeof(wxMarkerType
));
553 char * wxDebugContext::EndMarkerPos (const char * buf
, const size_t size
)
555 return CallerMemPos (buf
) + PaddedSize (size
);
560 Slightly different as this takes a pointer to the start of the caller
561 requested region and returns a pointer to the start of the buffer.
563 char * wxDebugContext::StartPos (const char * caller
)
565 return ((char *) (caller
- wxDebugContext::PaddedSize (sizeof(wxMarkerType
)) -
566 wxDebugContext::PaddedSize (sizeof (wxMemStruct
))));
570 We may need padding between various parts of the allocated memory.
571 Given a size of memory, this returns the amount of memory which should
572 be allocated in order to allow for alignment of the following object.
574 I don't know how portable this stuff is, but it seems to work for me at
575 the moment. It would be real nice if I knew more about this!
577 size_t wxDebugContext::GetPadding (const size_t size
)
579 size_t pad
= size
% CalcAlignment ();
580 return (pad
) ? sizeof(wxMarkerType
) - pad
: 0;
585 size_t wxDebugContext::PaddedSize (const size_t size
)
587 return size
+ GetPadding (size
);
591 Returns the total amount of memory which we need to get from the system
592 in order to satisfy a caller request. This includes space for the struct
593 plus markers and the caller's memory as well.
595 size_t wxDebugContext::TotSize (const size_t reqSize
)
597 return (PaddedSize (sizeof (wxMemStruct
)) + PaddedSize (reqSize
) +
598 2 * sizeof(wxMarkerType
));
603 Traverse the list of nodes executing the given function on each node.
605 void wxDebugContext::TraverseList (PmSFV func
, wxMemStruct
*from
)
608 from
= wxDebugContext::GetHead ();
610 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
612 void* data
= st
->GetActualData();
613 if ((data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
624 bool wxDebugContext::PrintList (void)
630 TraverseList ((PmSFV
)&wxMemStruct::PrintNode
, (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
));
638 bool wxDebugContext::Dump(void)
646 char* appName
= "application";
647 wxString
appNameStr("");
650 appNameStr
= wxTheApp
->GetAppName();
651 appName
= (char*) (const char*) appNameStr
;
652 wxTrace("----- Memory dump of %s at %s -----\n", appName
, WXSTRINGCAST
wxNow() );
656 wxTrace( "----- Memory dump -----\n" );
659 TraverseList ((PmSFV
)&wxMemStruct::Dump
, (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
));
669 struct wxDebugStatsStruct
674 wxDebugStatsStruct
*next
;
677 static wxDebugStatsStruct
*FindStatsStruct(wxDebugStatsStruct
*st
, char *name
)
681 if (strcmp(st
->instanceClass
, name
) == 0)
688 static wxDebugStatsStruct
*InsertStatsStruct(wxDebugStatsStruct
*head
, wxDebugStatsStruct
*st
)
694 bool wxDebugContext::PrintStatistics(bool detailed
)
702 char* appName
= "application";
703 wxString
appNameStr("");
706 appNameStr
= wxTheApp
->GetAppName();
707 appName
= (char*) (const char*) appNameStr
;
708 wxTrace("----- Memory statistics of %s at %s -----\n", appName
, WXSTRINGCAST
wxNow() );
712 wxTrace( "----- Memory statistics -----\n" );
716 bool currentMode
= GetDebugMode();
719 long noNonObjectNodes
= 0;
720 long noObjectNodes
= 0;
723 wxDebugStatsStruct
*list
= NULL
;
725 wxMemStruct
*from
= (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
);
727 from
= wxDebugContext::GetHead ();
730 for (st
= from
; st
!= 0; st
= st
->m_next
)
732 void* data
= st
->GetActualData();
733 if (detailed
&& (data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
735 char *className
= "nonobject";
736 if (st
->m_isObject
&& st
->GetActualData())
738 wxObject
*obj
= (wxObject
*)st
->GetActualData();
739 if (obj
->GetClassInfo()->GetClassName())
740 className
= obj
->GetClassInfo()->GetClassName();
742 wxDebugStatsStruct
*stats
= FindStatsStruct(list
, className
);
745 stats
= (wxDebugStatsStruct
*)malloc(sizeof(wxDebugStatsStruct
));
746 stats
->instanceClass
= className
;
747 stats
->instanceCount
= 0;
748 stats
->totalSize
= 0;
749 list
= InsertStatsStruct(list
, stats
);
751 stats
->instanceCount
++;
752 stats
->totalSize
+= st
->RequestSize();
755 if ((data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
757 totalSize
+= st
->RequestSize();
769 wxTrace("%ld objects of class %s, total size %ld\n",
770 list
->instanceCount
, list
->instanceClass
, list
->totalSize
);
771 wxDebugStatsStruct
*old
= list
;
778 SetDebugMode(currentMode
);
780 wxTrace("Number of object items: %ld\n", noObjectNodes
);
781 wxTrace("Number of non-object items: %ld\n", noNonObjectNodes
);
782 wxTrace("Total allocated size: %ld\n", totalSize
);
791 bool wxDebugContext::PrintClasses(void)
798 char* appName
= "application";
799 wxString
appNameStr("");
802 appNameStr
= wxTheApp
->GetAppName();
803 appName
= (char*) (const char*) appNameStr
;
804 wxTrace("----- Classes in %s -----\n", appName
);
812 wxClassInfo::sm_classTable
->BeginFind();
813 node
= wxClassInfo::sm_classTable
->Next();
816 info
= (wxClassInfo
*)node
->Data();
817 if (info
->GetClassName())
819 wxTrace("%s ", info
->GetClassName());
821 if (info
->GetBaseClassName1() && !info
->GetBaseClassName2())
822 wxTrace("is a %s", info
->GetBaseClassName1());
823 else if (info
->GetBaseClassName1() && info
->GetBaseClassName2())
824 wxTrace("is a %s, %s", info
->GetBaseClassName1(), info
->GetBaseClassName2());
825 if (info
->GetConstructor())
826 wxTrace(": dynamic\n");
830 node
= wxClassInfo::sm_classTable
->Next();
833 wxTrace("\nThere are %d classes derived from wxObject.\n\n\n", n
);
837 void wxDebugContext::SetCheckpoint(bool all
)
845 // Checks all nodes since checkpoint, or since start.
846 int wxDebugContext::Check(bool checkAll
)
850 wxMemStruct
*from
= (checkPoint
? checkPoint
->m_next
: (wxMemStruct
*)NULL
);
851 if (!from
|| checkAll
)
852 from
= wxDebugContext::GetHead ();
854 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
857 nFailures
+= st
->CheckBlock ();
865 // Count the number of non-wxDebugContext-related objects
866 // that are outstanding
867 int wxDebugContext::CountObjectsLeft(void)
871 wxMemStruct
*from
= wxDebugContext::GetHead ();
873 for (wxMemStruct
* st
= from
; st
!= 0; st
= st
->m_next
)
875 void* data
= st
->GetActualData();
876 if ((data
!= (void*)m_debugStream
) && (data
!= (void*) m_streamBuf
))
884 The global operator new used for everything apart from getting
885 dynamic storage within this function itself.
888 // We'll only do malloc and free for the moment: leave the interesting
889 // stuff for the wxObject versions.
891 #if WXDEBUG && USE_GLOBAL_MEMORY_OPERATORS
897 // Seems OK all of a sudden. Maybe to do with linking with multithreaded library?
898 #if 0 // def _MSC_VER
899 #define NO_DEBUG_ALLOCATION
902 // Unfortunately ~wxDebugStreamBuf doesn't work (VC++ 5) when we enable the debugging
903 // code. I have no idea why. In BC++ 4.5, we have a similar problem the debug
904 // stream myseriously changing pointer address between being passed from SetFile to SetStream.
905 // See docs/msw/issues.txt.
906 void * operator new (size_t size
, char * fileName
, int lineNum
)
908 #ifdef NO_DEBUG_ALLOCATION
911 return wxDebugAlloc(size
, fileName
, lineNum
, FALSE
, FALSE
);
915 #if !( defined (_MSC_VER) && (_MSC_VER <= 1000) )
916 void * operator new[] (size_t size
, char * fileName
, int lineNum
)
918 #ifdef NO_DEBUG_ALLOCATION
921 return wxDebugAlloc(size
, fileName
, lineNum
, FALSE
, TRUE
);
926 void operator delete (void * buf
)
928 #ifdef NO_DEBUG_ALLOCATION
935 #if !( defined (_MSC_VER) && (_MSC_VER <= 1000) )
936 void operator delete[] (void * buf
)
938 #ifdef NO_DEBUG_ALLOCATION
941 wxDebugFree(buf
, TRUE
);
948 // TODO: store whether this is a vector or not.
949 void * wxDebugAlloc(size_t size
, char * fileName
, int lineNum
, bool isObject
, bool WXUNUSED(isVect
) )
951 // If not in debugging allocation mode, do the normal thing
952 // so we don't leave any trace of ourselves in the node list.
954 if (!wxDebugContext::GetDebugMode())
956 return (void *)malloc(size
);
959 char * buf
= (char *) malloc(wxDebugContext::TotSize (size
));
961 wxTrace("Call to malloc (%ld) failed.\n", (long)size
);
964 wxMemStruct
* st
= (wxMemStruct
*)buf
;
965 st
->m_firstMarker
= MemStartCheck
;
966 st
->m_reqSize
= size
;
967 st
->m_fileName
= fileName
;
968 st
->m_lineNum
= lineNum
;
969 st
->m_id
= MemStructId
;
972 st
->m_isObject
= isObject
;
974 // Errors from Append() shouldn't really happen - but just in case!
975 if (st
->Append () == 0) {
976 st
->ErrorMsg ("Trying to append new node");
979 if (wxDebugContext::GetCheckPrevious ()) {
980 if (st
->CheckAllPrevious () < 0) {
981 st
->ErrorMsg ("Checking previous nodes");
985 // Set up the extra markers at the middle and end.
986 char * ptr
= wxDebugContext::MidMarkerPos (buf
);
987 * (wxMarkerType
*) ptr
= MemMidCheck
;
988 ptr
= wxDebugContext::EndMarkerPos (buf
, size
);
989 * (wxMarkerType
*) ptr
= MemEndCheck
;
991 // pointer returned points to the start of the caller's
993 void *m_actualData
= (void *) wxDebugContext::CallerMemPos (buf
);
994 st
->m_actualData
= m_actualData
;
999 // TODO: check whether was allocated as a vector
1000 void wxDebugFree(void * buf
, bool WXUNUSED(isVect
) )
1005 // If not in debugging allocation mode, do the normal thing
1006 // so we don't leave any trace of ourselves in the node list.
1007 if (!wxDebugContext::GetDebugMode())
1013 // Points to the start of the entire allocated area.
1014 char * startPointer
= wxDebugContext::StartPos ((char *) buf
);
1015 // Find the struct and make sure that it's identifiable.
1016 wxMemStruct
* st
= (wxMemStruct
*) wxDebugContext::StructPos (startPointer
);
1018 if (! st
->ValidateNode ())
1021 // If this is the current checkpoint, we need to
1022 // move the checkpoint back so it points to a valid
1024 if (st
== wxDebugContext::checkPoint
)
1025 wxDebugContext::checkPoint
= wxDebugContext::checkPoint
->m_prev
;
1027 if (! st
->Unlink ())
1029 st
->ErrorMsg ("Unlinking deleted node");
1032 // Now put in the fill char into the id slot and the caller requested
1033 // memory locations.
1035 (void) memset (wxDebugContext::CallerMemPos (startPointer
), MemFillChar
,
1036 st
->RequestSize ());
1038 // Don't allow delayed freeing of memory in this version
1039 // if (!wxDebugContext::GetDelayFree())
1040 // free((void *)st);
1044 // Trace: send output to the current debugging stream
1045 void wxTrace(const char *fmt
...)
1048 static char buffer
[512];
1053 wvsprintf(buffer
,fmt
,ap
) ;
1055 vsprintf(buffer
,fmt
,ap
) ;
1060 if (wxDebugContext::HasStream())
1062 wxDebugContext::GetStream() << buffer
;
1063 wxDebugContext::GetStream().flush();
1067 OutputDebugString((LPCSTR
)buffer
) ;
1069 fprintf(stderr
, buffer
);
1074 void wxTraceLevel(int level
, const char *fmt
...)
1076 if (wxDebugContext::GetLevel() < level
)
1080 static char buffer
[512];
1085 wvsprintf(buffer
,fmt
,ap
) ;
1087 vsprintf(buffer
,fmt
,ap
) ;
1092 if (wxDebugContext::HasStream())
1094 wxDebugContext::GetStream() << buffer
;
1095 wxDebugContext::GetStream().flush();
1099 OutputDebugString((LPCSTR
)buffer
) ;
1101 fprintf(stderr
, buffer
);
1105 #else // USE_MEMORY_TRACING && WXDEBUG
1106 void wxTrace(const char *WXUNUSED(fmt
) ...)
1110 void wxTraceLevel(int WXUNUSED(level
), const char *WXUNUSED(fmt
) ...)