1 |
william |
31 |
///////////////////////////////////////////////////////////////////////////// |
2 |
|
|
// Name: src/common/memory.cpp |
3 |
|
|
// Purpose: Memory checking implementation |
4 |
|
|
// Author: Arthur Seaton, Julian Smart |
5 |
|
|
// Modified by: |
6 |
|
|
// Created: 04/01/98 |
7 |
|
|
// RCS-ID: $Id: memory.cpp 41054 2006-09-07 19:01:45Z ABX $ |
8 |
|
|
// Copyright: (c) Julian Smart |
9 |
|
|
// Licence: wxWindows licence |
10 |
|
|
///////////////////////////////////////////////////////////////////////////// |
11 |
|
|
|
12 |
|
|
// For compilers that support precompilation, includes "wx.h". |
13 |
|
|
#include "wx/wxprec.h" |
14 |
|
|
|
15 |
|
|
#ifdef __BORLANDC__ |
16 |
|
|
#pragma hdrstop |
17 |
|
|
#endif |
18 |
|
|
|
19 |
|
|
#if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT |
20 |
|
|
|
21 |
|
|
#include "wx/memory.h" |
22 |
|
|
|
23 |
|
|
#ifndef WX_PRECOMP |
24 |
|
|
#ifdef __WXMSW__ |
25 |
|
|
#include "wx/msw/wrapwin.h" |
26 |
|
|
#endif |
27 |
|
|
#include "wx/utils.h" |
28 |
|
|
#include "wx/app.h" |
29 |
|
|
#include "wx/hash.h" |
30 |
|
|
#include "wx/log.h" |
31 |
|
|
#endif |
32 |
|
|
|
33 |
|
|
#if wxUSE_THREADS |
34 |
|
|
#include "wx/thread.h" |
35 |
|
|
#endif |
36 |
|
|
|
37 |
|
|
#include <stdlib.h> |
38 |
|
|
|
39 |
|
|
#include "wx/ioswrap.h" |
40 |
|
|
|
41 |
|
|
#if !defined(__WATCOMC__) && !(defined(__VMS__) && ( __VMS_VER < 70000000 ) )\ |
42 |
|
|
&& !defined( __MWERKS__ ) && !defined(__SALFORDC__) |
43 |
|
|
#include <memory.h> |
44 |
|
|
#endif |
45 |
|
|
|
46 |
|
|
#include <stdarg.h> |
47 |
|
|
#include <string.h> |
48 |
|
|
|
49 |
|
|
#if wxUSE_THREADS && defined(__WXDEBUG__) |
50 |
|
|
#define USE_THREADSAFE_MEMORY_ALLOCATION 1 |
51 |
|
|
#else |
52 |
|
|
#define USE_THREADSAFE_MEMORY_ALLOCATION 0 |
53 |
|
|
#endif |
54 |
|
|
|
55 |
|
|
|
56 |
|
|
#ifdef new |
57 |
|
|
#undef new |
58 |
|
|
#endif |
59 |
|
|
|
60 |
|
|
// wxDebugContext wxTheDebugContext; |
61 |
|
|
/* |
62 |
|
|
Redefine new and delete so that we can pick up situations where: |
63 |
|
|
- we overwrite or underwrite areas of malloc'd memory. |
64 |
|
|
- we use uninitialise variables |
65 |
|
|
Only do this in debug mode. |
66 |
|
|
|
67 |
|
|
We change new to get enough memory to allocate a struct, followed |
68 |
|
|
by the caller's requested memory, followed by a tag. The struct |
69 |
|
|
is used to create a doubly linked list of these areas and also |
70 |
|
|
contains another tag. The tags are used to determine when the area |
71 |
|
|
has been over/under written. |
72 |
|
|
*/ |
73 |
|
|
|
74 |
|
|
|
75 |
|
|
/* |
76 |
|
|
Values which are used to set the markers which will be tested for |
77 |
|
|
under/over write. There are 3 of these, one in the struct, one |
78 |
|
|
immediately after the struct but before the caller requested memory and |
79 |
|
|
one immediately after the requested memory. |
80 |
|
|
*/ |
81 |
|
|
#define MemStartCheck 0x23A8 |
82 |
|
|
#define MemMidCheck 0xA328 |
83 |
|
|
#define MemEndCheck 0x8A32 |
84 |
|
|
#define MemFillChar 0xAF |
85 |
|
|
#define MemStructId 0x666D |
86 |
|
|
|
87 |
|
|
/* |
88 |
|
|
External interface for the wxMemStruct class. Others are |
89 |
|
|
defined inline within the class def. Here we only need to be able |
90 |
|
|
to add and delete nodes from the list and handle errors in some way. |
91 |
|
|
*/ |
92 |
|
|
|
93 |
|
|
/* |
94 |
|
|
Used for internal "this shouldn't happen" type of errors. |
95 |
|
|
*/ |
96 |
|
|
void wxMemStruct::ErrorMsg (const char * mesg) |
97 |
|
|
{ |
98 |
|
|
wxLogMessage(wxT("wxWidgets memory checking error: %s"), mesg); |
99 |
|
|
PrintNode (); |
100 |
|
|
} |
101 |
|
|
|
102 |
|
|
/* |
103 |
|
|
Used when we find an overwrite or an underwrite error. |
104 |
|
|
*/ |
105 |
|
|
void wxMemStruct::ErrorMsg () |
106 |
|
|
{ |
107 |
|
|
wxLogMessage(wxT("wxWidgets over/underwrite memory error:")); |
108 |
|
|
PrintNode (); |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
|
112 |
|
|
/* |
113 |
|
|
We want to find out if pointers have been overwritten as soon as is |
114 |
|
|
possible, so test everything before we dereference it. Of course it's still |
115 |
|
|
quite possible that, if things have been overwritten, this function will |
116 |
|
|
fall over, but the only way of dealing with that would cost too much in terms |
117 |
|
|
of time. |
118 |
|
|
*/ |
119 |
|
|
int wxMemStruct::AssertList () |
120 |
|
|
{ |
121 |
|
|
if (wxDebugContext::GetHead () != 0 && ! (wxDebugContext::GetHead ())->AssertIt () || |
122 |
|
|
wxDebugContext::GetTail () != 0 && ! wxDebugContext::GetTail ()->AssertIt ()) { |
123 |
|
|
ErrorMsg ("Head or tail pointers trashed"); |
124 |
|
|
return 0; |
125 |
|
|
} |
126 |
|
|
return 1; |
127 |
|
|
} |
128 |
|
|
|
129 |
|
|
|
130 |
|
|
/* |
131 |
|
|
Check that the thing we're pointing to has the correct id for a wxMemStruct |
132 |
|
|
object and also that it's previous and next pointers are pointing at objects |
133 |
|
|
which have valid ids. |
134 |
|
|
This is definitely not perfect since we could fall over just trying to access |
135 |
|
|
any of the slots which we use here, but I think it's about the best that I |
136 |
|
|
can do without doing something like taking all new wxMemStruct pointers and |
137 |
|
|
comparing them against all known pointer within the list and then only |
138 |
|
|
doing this sort of check _after_ you've found the pointer in the list. That |
139 |
|
|
would be safer, but also much more time consuming. |
140 |
|
|
*/ |
141 |
|
|
int wxMemStruct::AssertIt () |
142 |
|
|
{ |
143 |
|
|
return (m_id == MemStructId && |
144 |
|
|
(m_prev == 0 || m_prev->m_id == MemStructId) && |
145 |
|
|
(m_next == 0 || m_next->m_id == MemStructId)); |
146 |
|
|
} |
147 |
|
|
|
148 |
|
|
|
149 |
|
|
/* |
150 |
|
|
Additions are always at the tail of the list. |
151 |
|
|
Returns 0 on error, non-zero on success. |
152 |
|
|
*/ |
153 |
|
|
int wxMemStruct::Append () |
154 |
|
|
{ |
155 |
|
|
if (! AssertList ()) |
156 |
|
|
return 0; |
157 |
|
|
|
158 |
|
|
if (wxDebugContext::GetHead () == 0) { |
159 |
|
|
if (wxDebugContext::GetTail () != 0) { |
160 |
|
|
ErrorMsg ("Null list should have a null tail pointer"); |
161 |
|
|
return 0; |
162 |
|
|
} |
163 |
|
|
(void) wxDebugContext::SetHead (this); |
164 |
|
|
(void) wxDebugContext::SetTail (this); |
165 |
|
|
} else { |
166 |
|
|
wxDebugContext::GetTail ()->m_next = this; |
167 |
|
|
this->m_prev = wxDebugContext::GetTail (); |
168 |
|
|
(void) wxDebugContext::SetTail (this); |
169 |
|
|
} |
170 |
|
|
return 1; |
171 |
|
|
} |
172 |
|
|
|
173 |
|
|
|
174 |
|
|
/* |
175 |
|
|
Don't actually free up anything here as the space which is used |
176 |
|
|
by the node will be free'd up when the whole block is free'd. |
177 |
|
|
Returns 0 on error, non-zero on success. |
178 |
|
|
*/ |
179 |
|
|
int wxMemStruct::Unlink () |
180 |
|
|
{ |
181 |
|
|
if (! AssertList ()) |
182 |
|
|
return 0; |
183 |
|
|
|
184 |
|
|
if (wxDebugContext::GetHead () == 0 || wxDebugContext::GetTail () == 0) { |
185 |
|
|
ErrorMsg ("Trying to remove node from empty list"); |
186 |
|
|
return 0; |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
// Handle the part of the list before this node. |
190 |
|
|
if (m_prev == 0) { |
191 |
|
|
if (this != wxDebugContext::GetHead ()) { |
192 |
|
|
ErrorMsg ("No previous node for non-head node"); |
193 |
|
|
return 0; |
194 |
|
|
} |
195 |
|
|
(void) wxDebugContext::SetHead (m_next); |
196 |
|
|
} else { |
197 |
|
|
if (! m_prev->AssertIt ()) { |
198 |
|
|
ErrorMsg ("Trashed previous pointer"); |
199 |
|
|
return 0; |
200 |
|
|
} |
201 |
|
|
|
202 |
|
|
if (m_prev->m_next != this) { |
203 |
|
|
ErrorMsg ("List is inconsistent"); |
204 |
|
|
return 0; |
205 |
|
|
} |
206 |
|
|
m_prev->m_next = m_next; |
207 |
|
|
} |
208 |
|
|
|
209 |
|
|
// Handle the part of the list after this node. |
210 |
|
|
if (m_next == 0) { |
211 |
|
|
if (this != wxDebugContext::GetTail ()) { |
212 |
|
|
ErrorMsg ("No next node for non-tail node"); |
213 |
|
|
return 0; |
214 |
|
|
} |
215 |
|
|
(void) wxDebugContext::SetTail (m_prev); |
216 |
|
|
} else { |
217 |
|
|
if (! m_next->AssertIt ()) { |
218 |
|
|
ErrorMsg ("Trashed next pointer"); |
219 |
|
|
return 0; |
220 |
|
|
} |
221 |
|
|
|
222 |
|
|
if (m_next->m_prev != this) { |
223 |
|
|
ErrorMsg ("List is inconsistent"); |
224 |
|
|
return 0; |
225 |
|
|
} |
226 |
|
|
m_next->m_prev = m_prev; |
227 |
|
|
} |
228 |
|
|
|
229 |
|
|
return 1; |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
|
233 |
|
|
|
234 |
|
|
/* |
235 |
|
|
Checks a node and block of memory to see that the markers are still |
236 |
|
|
intact. |
237 |
|
|
*/ |
238 |
|
|
int wxMemStruct::CheckBlock () |
239 |
|
|
{ |
240 |
|
|
int nFailures = 0; |
241 |
|
|
|
242 |
|
|
if (m_firstMarker != MemStartCheck) { |
243 |
|
|
nFailures++; |
244 |
|
|
ErrorMsg (); |
245 |
|
|
} |
246 |
|
|
|
247 |
|
|
char * pointer = wxDebugContext::MidMarkerPos ((char *) this); |
248 |
|
|
if (* (wxMarkerType *) pointer != MemMidCheck) { |
249 |
|
|
nFailures++; |
250 |
|
|
ErrorMsg (); |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
pointer = wxDebugContext::EndMarkerPos ((char *) this, RequestSize ()); |
254 |
|
|
if (* (wxMarkerType *) pointer != MemEndCheck) { |
255 |
|
|
nFailures++; |
256 |
|
|
ErrorMsg (); |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
return nFailures; |
260 |
|
|
} |
261 |
|
|
|
262 |
|
|
|
263 |
|
|
/* |
264 |
|
|
Check the list of nodes to see if they are all ok. |
265 |
|
|
*/ |
266 |
|
|
int wxMemStruct::CheckAllPrevious () |
267 |
|
|
{ |
268 |
|
|
int nFailures = 0; |
269 |
|
|
|
270 |
|
|
for (wxMemStruct * st = this->m_prev; st != 0; st = st->m_prev) { |
271 |
|
|
if (st->AssertIt ()) |
272 |
|
|
nFailures += st->CheckBlock (); |
273 |
|
|
else |
274 |
|
|
return -1; |
275 |
|
|
} |
276 |
|
|
|
277 |
|
|
return nFailures; |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
|
281 |
|
|
/* |
282 |
|
|
When we delete a node we set the id slot to a specific value and then test |
283 |
|
|
against this to see if a nodes have been deleted previously. I don't |
284 |
|
|
just set the entire memory to the fillChar because then I'd be overwriting |
285 |
|
|
useful stuff like the vtbl which may be needed to output the error message |
286 |
|
|
including the file name and line numbers. Without this info the whole point |
287 |
|
|
of this class is lost! |
288 |
|
|
*/ |
289 |
|
|
void wxMemStruct::SetDeleted () |
290 |
|
|
{ |
291 |
|
|
m_id = MemFillChar; |
292 |
|
|
} |
293 |
|
|
|
294 |
|
|
int wxMemStruct::IsDeleted () |
295 |
|
|
{ |
296 |
|
|
return (m_id == MemFillChar); |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
|
300 |
|
|
/* |
301 |
|
|
Print out a single node. There are many far better ways of doing this |
302 |
|
|
but this will suffice for now. |
303 |
|
|
*/ |
304 |
|
|
void wxMemStruct::PrintNode () |
305 |
|
|
{ |
306 |
|
|
if (m_isObject) |
307 |
|
|
{ |
308 |
|
|
wxObject *obj = (wxObject *)m_actualData; |
309 |
|
|
wxClassInfo *info = obj->GetClassInfo(); |
310 |
|
|
|
311 |
|
|
// Let's put this in standard form so IDEs can load the file at the appropriate |
312 |
|
|
// line |
313 |
|
|
wxString msg; |
314 |
|
|
|
315 |
|
|
if (m_fileName) |
316 |
|
|
msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum); |
317 |
|
|
|
318 |
|
|
if (info && info->GetClassName()) |
319 |
|
|
msg += info->GetClassName(); |
320 |
|
|
else |
321 |
|
|
msg += wxT("object"); |
322 |
|
|
|
323 |
|
|
wxString msg2; |
324 |
|
|
msg2.Printf(wxT(" at 0x%lX, size %d"), (long)GetActualData(), (int)RequestSize()); |
325 |
|
|
msg += msg2; |
326 |
|
|
|
327 |
|
|
wxLogMessage(msg); |
328 |
|
|
} |
329 |
|
|
else |
330 |
|
|
{ |
331 |
|
|
wxString msg; |
332 |
|
|
|
333 |
|
|
if (m_fileName) |
334 |
|
|
msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum); |
335 |
|
|
msg += wxT("non-object data"); |
336 |
|
|
wxString msg2; |
337 |
|
|
msg2.Printf(wxT(" at 0x%lX, size %d\n"), (long)GetActualData(), (int)RequestSize()); |
338 |
|
|
msg += msg2; |
339 |
|
|
|
340 |
|
|
wxLogMessage(msg); |
341 |
|
|
} |
342 |
|
|
} |
343 |
|
|
|
344 |
|
|
void wxMemStruct::Dump () |
345 |
|
|
{ |
346 |
|
|
if (!ValidateNode()) return; |
347 |
|
|
|
348 |
|
|
if (m_isObject) |
349 |
|
|
{ |
350 |
|
|
wxObject *obj = (wxObject *)m_actualData; |
351 |
|
|
|
352 |
|
|
wxString msg; |
353 |
|
|
if (m_fileName) |
354 |
|
|
msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum); |
355 |
|
|
|
356 |
|
|
|
357 |
|
|
/* TODO: We no longer have a stream (using wxLogDebug) so we can't dump it. |
358 |
|
|
* Instead, do what wxObject::Dump does. |
359 |
|
|
* What should we do long-term, eliminate Dumping? Or specify |
360 |
|
|
* that MyClass::Dump should use wxLogDebug? Ugh. |
361 |
|
|
obj->Dump(wxDebugContext::GetStream()); |
362 |
|
|
*/ |
363 |
|
|
|
364 |
|
|
if (obj->GetClassInfo() && obj->GetClassInfo()->GetClassName()) |
365 |
|
|
msg += obj->GetClassInfo()->GetClassName(); |
366 |
|
|
else |
367 |
|
|
msg += wxT("unknown object class"); |
368 |
|
|
|
369 |
|
|
wxString msg2; |
370 |
|
|
msg2.Printf(wxT(" at 0x%lX, size %d"), (long)GetActualData(), (int)RequestSize()); |
371 |
|
|
msg += msg2; |
372 |
|
|
|
373 |
|
|
wxDebugContext::OutputDumpLine(msg); |
374 |
|
|
} |
375 |
|
|
else |
376 |
|
|
{ |
377 |
|
|
wxString msg; |
378 |
|
|
if (m_fileName) |
379 |
|
|
msg.Printf(wxT("%s(%d): "), m_fileName, (int)m_lineNum); |
380 |
|
|
|
381 |
|
|
wxString msg2; |
382 |
|
|
msg2.Printf(wxT("non-object data at 0x%lX, size %d"), (long)GetActualData(), (int)RequestSize() ); |
383 |
|
|
msg += msg2; |
384 |
|
|
wxDebugContext::OutputDumpLine(msg); |
385 |
|
|
} |
386 |
|
|
} |
387 |
|
|
|
388 |
|
|
|
389 |
|
|
/* |
390 |
|
|
Validate a node. Check to see that the node is "clean" in the sense |
391 |
|
|
that nothing has over/underwritten it etc. |
392 |
|
|
*/ |
393 |
|
|
int wxMemStruct::ValidateNode () |
394 |
|
|
{ |
395 |
|
|
char * startPointer = (char *) this; |
396 |
|
|
if (!AssertIt ()) { |
397 |
|
|
if (IsDeleted ()) |
398 |
|
|
ErrorMsg ("Object already deleted"); |
399 |
|
|
else { |
400 |
|
|
// Can't use the error routines as we have no recognisable object. |
401 |
|
|
#ifndef __WXGTK__ |
402 |
|
|
wxLogMessage(wxT("Can't verify memory struct - all bets are off!")); |
403 |
|
|
#endif |
404 |
|
|
} |
405 |
|
|
return 0; |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
/* |
409 |
|
|
int i; |
410 |
|
|
for (i = 0; i < wxDebugContext::TotSize (requestSize ()); i++) |
411 |
|
|
cout << startPointer [i]; |
412 |
|
|
cout << endl; |
413 |
|
|
*/ |
414 |
|
|
if (Marker () != MemStartCheck) |
415 |
|
|
ErrorMsg (); |
416 |
|
|
if (* (wxMarkerType *) wxDebugContext::MidMarkerPos (startPointer) != MemMidCheck) |
417 |
|
|
ErrorMsg (); |
418 |
|
|
if (* (wxMarkerType *) wxDebugContext::EndMarkerPos (startPointer, |
419 |
|
|
RequestSize ()) != |
420 |
|
|
MemEndCheck) |
421 |
|
|
ErrorMsg (); |
422 |
|
|
|
423 |
|
|
// Back to before the extra buffer and check that |
424 |
|
|
// we can still read what we originally wrote. |
425 |
|
|
if (Marker () != MemStartCheck || |
426 |
|
|
* (wxMarkerType *) wxDebugContext::MidMarkerPos (startPointer) |
427 |
|
|
!= MemMidCheck || |
428 |
|
|
* (wxMarkerType *) wxDebugContext::EndMarkerPos (startPointer, |
429 |
|
|
RequestSize ()) != MemEndCheck) |
430 |
|
|
{ |
431 |
|
|
ErrorMsg (); |
432 |
|
|
return 0; |
433 |
|
|
} |
434 |
|
|
|
435 |
|
|
return 1; |
436 |
|
|
} |
437 |
|
|
|
438 |
|
|
/* |
439 |
|
|
The wxDebugContext class. |
440 |
|
|
*/ |
441 |
|
|
|
442 |
|
|
wxMemStruct *wxDebugContext::m_head = NULL; |
443 |
|
|
wxMemStruct *wxDebugContext::m_tail = NULL; |
444 |
|
|
|
445 |
|
|
bool wxDebugContext::m_checkPrevious = false; |
446 |
|
|
int wxDebugContext::debugLevel = 1; |
447 |
|
|
bool wxDebugContext::debugOn = true; |
448 |
|
|
wxMemStruct *wxDebugContext::checkPoint = NULL; |
449 |
|
|
|
450 |
|
|
// For faster alignment calculation |
451 |
|
|
static wxMarkerType markerCalc[2]; |
452 |
|
|
int wxDebugContext::m_balign = (int)((char *)&markerCalc[1] - (char*)&markerCalc[0]); |
453 |
|
|
int wxDebugContext::m_balignmask = (int)((char *)&markerCalc[1] - (char*)&markerCalc[0]) - 1; |
454 |
|
|
|
455 |
|
|
wxDebugContext::wxDebugContext(void) |
456 |
|
|
{ |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
wxDebugContext::~wxDebugContext(void) |
460 |
|
|
{ |
461 |
|
|
} |
462 |
|
|
|
463 |
|
|
/* |
464 |
|
|
Work out the positions of the markers by creating an array of 2 markers |
465 |
|
|
and comparing the addresses of the 2 elements. Use this number as the |
466 |
|
|
alignment for markers. |
467 |
|
|
*/ |
468 |
|
|
size_t wxDebugContext::CalcAlignment () |
469 |
|
|
{ |
470 |
|
|
wxMarkerType ar[2]; |
471 |
|
|
return (char *) &ar[1] - (char *) &ar[0]; |
472 |
|
|
} |
473 |
|
|
|
474 |
|
|
|
475 |
|
|
char * wxDebugContext::StructPos (const char * buf) |
476 |
|
|
{ |
477 |
|
|
return (char *) buf; |
478 |
|
|
} |
479 |
|
|
|
480 |
|
|
char * wxDebugContext::MidMarkerPos (const char * buf) |
481 |
|
|
{ |
482 |
|
|
return StructPos (buf) + PaddedSize (sizeof (wxMemStruct)); |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
char * wxDebugContext::CallerMemPos (const char * buf) |
486 |
|
|
{ |
487 |
|
|
return MidMarkerPos (buf) + PaddedSize (sizeof(wxMarkerType)); |
488 |
|
|
} |
489 |
|
|
|
490 |
|
|
|
491 |
|
|
char * wxDebugContext::EndMarkerPos (const char * buf, const size_t size) |
492 |
|
|
{ |
493 |
|
|
return CallerMemPos (buf) + PaddedSize (size); |
494 |
|
|
} |
495 |
|
|
|
496 |
|
|
|
497 |
|
|
/* |
498 |
|
|
Slightly different as this takes a pointer to the start of the caller |
499 |
|
|
requested region and returns a pointer to the start of the buffer. |
500 |
|
|
*/ |
501 |
|
|
char * wxDebugContext::StartPos (const char * caller) |
502 |
|
|
{ |
503 |
|
|
return ((char *) (caller - wxDebugContext::PaddedSize (sizeof(wxMarkerType)) - |
504 |
|
|
wxDebugContext::PaddedSize (sizeof (wxMemStruct)))); |
505 |
|
|
} |
506 |
|
|
|
507 |
|
|
/* |
508 |
|
|
We may need padding between various parts of the allocated memory. |
509 |
|
|
Given a size of memory, this returns the amount of memory which should |
510 |
|
|
be allocated in order to allow for alignment of the following object. |
511 |
|
|
|
512 |
|
|
I don't know how portable this stuff is, but it seems to work for me at |
513 |
|
|
the moment. It would be real nice if I knew more about this! |
514 |
|
|
|
515 |
|
|
// Note: this function is now obsolete (along with CalcAlignment) |
516 |
|
|
// because the calculations are done statically, for greater speed. |
517 |
|
|
*/ |
518 |
|
|
size_t wxDebugContext::GetPadding (const size_t size) |
519 |
|
|
{ |
520 |
|
|
size_t pad = size % CalcAlignment (); |
521 |
|
|
return (pad) ? sizeof(wxMarkerType) - pad : 0; |
522 |
|
|
} |
523 |
|
|
|
524 |
|
|
size_t wxDebugContext::PaddedSize (const size_t size) |
525 |
|
|
{ |
526 |
|
|
// Added by Terry Farnham <TJRT@pacbell.net> to replace |
527 |
|
|
// slow GetPadding call. |
528 |
|
|
int padb; |
529 |
|
|
|
530 |
|
|
padb = size & m_balignmask; |
531 |
|
|
if(padb) |
532 |
|
|
return(size + m_balign - padb); |
533 |
|
|
else |
534 |
|
|
return(size); |
535 |
|
|
} |
536 |
|
|
|
537 |
|
|
/* |
538 |
|
|
Returns the total amount of memory which we need to get from the system |
539 |
|
|
in order to satisfy a caller request. This includes space for the struct |
540 |
|
|
plus markers and the caller's memory as well. |
541 |
|
|
*/ |
542 |
|
|
size_t wxDebugContext::TotSize (const size_t reqSize) |
543 |
|
|
{ |
544 |
|
|
return (PaddedSize (sizeof (wxMemStruct)) + PaddedSize (reqSize) + |
545 |
|
|
2 * sizeof(wxMarkerType)); |
546 |
|
|
} |
547 |
|
|
|
548 |
|
|
|
549 |
|
|
/* |
550 |
|
|
Traverse the list of nodes executing the given function on each node. |
551 |
|
|
*/ |
552 |
|
|
void wxDebugContext::TraverseList (PmSFV func, wxMemStruct *from) |
553 |
|
|
{ |
554 |
|
|
if (!from) |
555 |
|
|
from = wxDebugContext::GetHead (); |
556 |
|
|
|
557 |
|
|
wxMemStruct * st = NULL; |
558 |
|
|
for (st = from; st != 0; st = st->m_next) |
559 |
|
|
{ |
560 |
|
|
void* data = st->GetActualData(); |
561 |
|
|
// if ((data != (void*)m_debugStream) && (data != (void*) m_streamBuf)) |
562 |
|
|
if (data != (void*) wxLog::GetActiveTarget()) |
563 |
|
|
{ |
564 |
|
|
(st->*func) (); |
565 |
|
|
} |
566 |
|
|
} |
567 |
|
|
} |
568 |
|
|
|
569 |
|
|
|
570 |
|
|
/* |
571 |
|
|
Print out the list. |
572 |
|
|
*/ |
573 |
|
|
bool wxDebugContext::PrintList (void) |
574 |
|
|
{ |
575 |
|
|
#ifdef __WXDEBUG__ |
576 |
|
|
TraverseList ((PmSFV)&wxMemStruct::PrintNode, (checkPoint ? checkPoint->m_next : (wxMemStruct*)NULL)); |
577 |
|
|
|
578 |
|
|
return true; |
579 |
|
|
#else |
580 |
|
|
return false; |
581 |
|
|
#endif |
582 |
|
|
} |
583 |
|
|
|
584 |
|
|
bool wxDebugContext::Dump(void) |
585 |
|
|
{ |
586 |
|
|
#ifdef __WXDEBUG__ |
587 |
|
|
{ |
588 |
|
|
wxChar* appName = (wxChar*) wxT("application"); |
589 |
|
|
wxString appNameStr; |
590 |
|
|
if (wxTheApp) |
591 |
|
|
{ |
592 |
|
|
appNameStr = wxTheApp->GetAppName(); |
593 |
|
|
appName = WXSTRINGCAST appNameStr; |
594 |
|
|
OutputDumpLine(wxT("----- Memory dump of %s at %s -----"), appName, WXSTRINGCAST wxNow() ); |
595 |
|
|
} |
596 |
|
|
else |
597 |
|
|
{ |
598 |
|
|
OutputDumpLine( wxT("----- Memory dump -----") ); |
599 |
|
|
} |
600 |
|
|
} |
601 |
|
|
|
602 |
|
|
TraverseList ((PmSFV)&wxMemStruct::Dump, (checkPoint ? checkPoint->m_next : (wxMemStruct*)NULL)); |
603 |
|
|
|
604 |
|
|
OutputDumpLine(wxEmptyString); |
605 |
|
|
OutputDumpLine(wxEmptyString); |
606 |
|
|
|
607 |
|
|
return true; |
608 |
|
|
#else |
609 |
|
|
return false; |
610 |
|
|
#endif |
611 |
|
|
} |
612 |
|
|
|
613 |
|
|
#ifdef __WXDEBUG__ |
614 |
|
|
struct wxDebugStatsStruct |
615 |
|
|
{ |
616 |
|
|
long instanceCount; |
617 |
|
|
long totalSize; |
618 |
|
|
wxChar *instanceClass; |
619 |
|
|
wxDebugStatsStruct *next; |
620 |
|
|
}; |
621 |
|
|
|
622 |
|
|
static wxDebugStatsStruct *FindStatsStruct(wxDebugStatsStruct *st, wxChar *name) |
623 |
|
|
{ |
624 |
|
|
while (st) |
625 |
|
|
{ |
626 |
|
|
if (wxStrcmp(st->instanceClass, name) == 0) |
627 |
|
|
return st; |
628 |
|
|
st = st->next; |
629 |
|
|
} |
630 |
|
|
return NULL; |
631 |
|
|
} |
632 |
|
|
|
633 |
|
|
static wxDebugStatsStruct *InsertStatsStruct(wxDebugStatsStruct *head, wxDebugStatsStruct *st) |
634 |
|
|
{ |
635 |
|
|
st->next = head; |
636 |
|
|
return st; |
637 |
|
|
} |
638 |
|
|
#endif |
639 |
|
|
|
640 |
|
|
bool wxDebugContext::PrintStatistics(bool detailed) |
641 |
|
|
{ |
642 |
|
|
#ifdef __WXDEBUG__ |
643 |
|
|
{ |
644 |
|
|
wxChar* appName = (wxChar*) wxT("application"); |
645 |
|
|
wxString appNameStr; |
646 |
|
|
if (wxTheApp) |
647 |
|
|
{ |
648 |
|
|
appNameStr = wxTheApp->GetAppName(); |
649 |
|
|
appName = WXSTRINGCAST appNameStr; |
650 |
|
|
OutputDumpLine(wxT("----- Memory statistics of %s at %s -----"), appName, WXSTRINGCAST wxNow() ); |
651 |
|
|
} |
652 |
|
|
else |
653 |
|
|
{ |
654 |
|
|
OutputDumpLine( wxT("----- Memory statistics -----") ); |
655 |
|
|
} |
656 |
|
|
} |
657 |
|
|
|
658 |
|
|
bool currentMode = GetDebugMode(); |
659 |
|
|
SetDebugMode(false); |
660 |
|
|
|
661 |
|
|
long noNonObjectNodes = 0; |
662 |
|
|
long noObjectNodes = 0; |
663 |
|
|
long totalSize = 0; |
664 |
|
|
|
665 |
|
|
wxDebugStatsStruct *list = NULL; |
666 |
|
|
|
667 |
|
|
wxMemStruct *from = (checkPoint ? checkPoint->m_next : (wxMemStruct*)NULL ); |
668 |
|
|
if (!from) |
669 |
|
|
from = wxDebugContext::GetHead (); |
670 |
|
|
|
671 |
|
|
wxMemStruct *st; |
672 |
|
|
for (st = from; st != 0; st = st->m_next) |
673 |
|
|
{ |
674 |
|
|
void* data = st->GetActualData(); |
675 |
|
|
if (detailed && (data != (void*) wxLog::GetActiveTarget())) |
676 |
|
|
{ |
677 |
|
|
wxChar *className = (wxChar*) wxT("nonobject"); |
678 |
|
|
if (st->m_isObject && st->GetActualData()) |
679 |
|
|
{ |
680 |
|
|
wxObject *obj = (wxObject *)st->GetActualData(); |
681 |
|
|
if (obj->GetClassInfo()->GetClassName()) |
682 |
|
|
className = (wxChar*)obj->GetClassInfo()->GetClassName(); |
683 |
|
|
} |
684 |
|
|
wxDebugStatsStruct *stats = FindStatsStruct(list, className); |
685 |
|
|
if (!stats) |
686 |
|
|
{ |
687 |
|
|
stats = (wxDebugStatsStruct *)malloc(sizeof(wxDebugStatsStruct)); |
688 |
|
|
stats->instanceClass = className; |
689 |
|
|
stats->instanceCount = 0; |
690 |
|
|
stats->totalSize = 0; |
691 |
|
|
list = InsertStatsStruct(list, stats); |
692 |
|
|
} |
693 |
|
|
stats->instanceCount ++; |
694 |
|
|
stats->totalSize += st->RequestSize(); |
695 |
|
|
} |
696 |
|
|
|
697 |
|
|
if (data != (void*) wxLog::GetActiveTarget()) |
698 |
|
|
{ |
699 |
|
|
totalSize += st->RequestSize(); |
700 |
|
|
if (st->m_isObject) |
701 |
|
|
noObjectNodes ++; |
702 |
|
|
else |
703 |
|
|
noNonObjectNodes ++; |
704 |
|
|
} |
705 |
|
|
} |
706 |
|
|
|
707 |
|
|
if (detailed) |
708 |
|
|
{ |
709 |
|
|
while (list) |
710 |
|
|
{ |
711 |
|
|
OutputDumpLine(wxT("%ld objects of class %s, total size %ld"), |
712 |
|
|
list->instanceCount, list->instanceClass, list->totalSize); |
713 |
|
|
wxDebugStatsStruct *old = list; |
714 |
|
|
list = old->next; |
715 |
|
|
free((char *)old); |
716 |
|
|
} |
717 |
|
|
OutputDumpLine(wxEmptyString); |
718 |
|
|
} |
719 |
|
|
|
720 |
|
|
SetDebugMode(currentMode); |
721 |
|
|
|
722 |
|
|
OutputDumpLine(wxT("Number of object items: %ld"), noObjectNodes); |
723 |
|
|
OutputDumpLine(wxT("Number of non-object items: %ld"), noNonObjectNodes); |
724 |
|
|
OutputDumpLine(wxT("Total allocated size: %ld"), totalSize); |
725 |
|
|
OutputDumpLine(wxEmptyString); |
726 |
|
|
OutputDumpLine(wxEmptyString); |
727 |
|
|
|
728 |
|
|
return true; |
729 |
|
|
#else |
730 |
|
|
(void)detailed; |
731 |
|
|
return false; |
732 |
|
|
#endif |
733 |
|
|
} |
734 |
|
|
|
735 |
|
|
bool wxDebugContext::PrintClasses(void) |
736 |
|
|
{ |
737 |
|
|
{ |
738 |
|
|
wxChar* appName = (wxChar*) wxT("application"); |
739 |
|
|
wxString appNameStr; |
740 |
|
|
if (wxTheApp) |
741 |
|
|
{ |
742 |
|
|
appNameStr = wxTheApp->GetAppName(); |
743 |
|
|
appName = WXSTRINGCAST appNameStr; |
744 |
|
|
wxLogMessage(wxT("----- Classes in %s -----"), appName); |
745 |
|
|
} |
746 |
|
|
} |
747 |
|
|
|
748 |
|
|
int n = 0; |
749 |
|
|
wxHashTable::compatibility_iterator node; |
750 |
|
|
wxClassInfo *info; |
751 |
|
|
|
752 |
|
|
wxClassInfo::sm_classTable->BeginFind(); |
753 |
|
|
node = wxClassInfo::sm_classTable->Next(); |
754 |
|
|
while (node) |
755 |
|
|
{ |
756 |
|
|
info = (wxClassInfo *)node->GetData(); |
757 |
|
|
if (info->GetClassName()) |
758 |
|
|
{ |
759 |
|
|
wxString msg(info->GetClassName()); |
760 |
|
|
msg += wxT(" "); |
761 |
|
|
|
762 |
|
|
if (info->GetBaseClassName1() && !info->GetBaseClassName2()) |
763 |
|
|
{ |
764 |
|
|
msg += wxT("is a "); |
765 |
|
|
msg += info->GetBaseClassName1(); |
766 |
|
|
} |
767 |
|
|
else if (info->GetBaseClassName1() && info->GetBaseClassName2()) |
768 |
|
|
{ |
769 |
|
|
msg += wxT("is a "); |
770 |
|
|
msg += info->GetBaseClassName1() ; |
771 |
|
|
msg += wxT(", "); |
772 |
|
|
msg += info->GetBaseClassName2() ; |
773 |
|
|
} |
774 |
|
|
if (info->GetConstructor()) |
775 |
|
|
msg += wxT(": dynamic"); |
776 |
|
|
|
777 |
|
|
wxLogMessage(msg); |
778 |
|
|
} |
779 |
|
|
node = wxClassInfo::sm_classTable->Next(); |
780 |
|
|
n ++; |
781 |
|
|
} |
782 |
|
|
wxLogMessage(wxEmptyString); |
783 |
|
|
wxLogMessage(wxT("There are %d classes derived from wxObject."), n); |
784 |
|
|
wxLogMessage(wxEmptyString); |
785 |
|
|
wxLogMessage(wxEmptyString); |
786 |
|
|
return true; |
787 |
|
|
} |
788 |
|
|
|
789 |
|
|
void wxDebugContext::SetCheckpoint(bool all) |
790 |
|
|
{ |
791 |
|
|
if (all) |
792 |
|
|
checkPoint = NULL; |
793 |
|
|
else |
794 |
|
|
checkPoint = m_tail; |
795 |
|
|
} |
796 |
|
|
|
797 |
|
|
// Checks all nodes since checkpoint, or since start. |
798 |
|
|
int wxDebugContext::Check(bool checkAll) |
799 |
|
|
{ |
800 |
|
|
int nFailures = 0; |
801 |
|
|
|
802 |
|
|
wxMemStruct *from = (checkPoint ? checkPoint->m_next : (wxMemStruct*)NULL ); |
803 |
|
|
if (!from || checkAll) |
804 |
|
|
from = wxDebugContext::GetHead (); |
805 |
|
|
|
806 |
|
|
for (wxMemStruct * st = from; st != 0; st = st->m_next) |
807 |
|
|
{ |
808 |
|
|
if (st->AssertIt ()) |
809 |
|
|
nFailures += st->CheckBlock (); |
810 |
|
|
else |
811 |
|
|
return -1; |
812 |
|
|
} |
813 |
|
|
|
814 |
|
|
return nFailures; |
815 |
|
|
} |
816 |
|
|
|
817 |
|
|
// Count the number of non-wxDebugContext-related objects |
818 |
|
|
// that are outstanding |
819 |
|
|
int wxDebugContext::CountObjectsLeft(bool sinceCheckpoint) |
820 |
|
|
{ |
821 |
|
|
int n = 0; |
822 |
|
|
|
823 |
|
|
wxMemStruct *from = NULL; |
824 |
|
|
if (sinceCheckpoint && checkPoint) |
825 |
|
|
from = checkPoint->m_next; |
826 |
|
|
else |
827 |
|
|
from = wxDebugContext::GetHead () ; |
828 |
|
|
|
829 |
|
|
for (wxMemStruct * st = from; st != 0; st = st->m_next) |
830 |
|
|
{ |
831 |
|
|
void* data = st->GetActualData(); |
832 |
|
|
if (data != (void*) wxLog::GetActiveTarget()) |
833 |
|
|
n ++; |
834 |
|
|
} |
835 |
|
|
|
836 |
|
|
return n ; |
837 |
|
|
} |
838 |
|
|
|
839 |
|
|
// This function is used to output the dump |
840 |
|
|
void wxDebugContext::OutputDumpLine(const wxChar *szFormat, ...) |
841 |
|
|
{ |
842 |
|
|
// a buffer of 2048 bytes should be long enough for a file name |
843 |
|
|
// and a class name |
844 |
|
|
wxChar buf[2048]; |
845 |
|
|
int count; |
846 |
|
|
va_list argptr; |
847 |
|
|
va_start(argptr, szFormat); |
848 |
|
|
buf[sizeof(buf)/sizeof(wxChar)-1] = _T('\0'); |
849 |
|
|
|
850 |
|
|
// keep 3 bytes for a \r\n\0 |
851 |
|
|
count = wxVsnprintf(buf, sizeof(buf)/sizeof(wxChar)-3, szFormat, argptr); |
852 |
|
|
|
853 |
|
|
if ( count < 0 ) |
854 |
|
|
count = sizeof(buf)/sizeof(wxChar)-3; |
855 |
|
|
buf[count]=_T('\r'); |
856 |
|
|
buf[count+1]=_T('\n'); |
857 |
|
|
buf[count+2]=_T('\0'); |
858 |
|
|
|
859 |
|
|
wxMessageOutputDebug dbgout; |
860 |
|
|
dbgout.Printf(buf); |
861 |
|
|
} |
862 |
|
|
|
863 |
|
|
|
864 |
|
|
#if USE_THREADSAFE_MEMORY_ALLOCATION |
865 |
|
|
static bool memSectionOk = false; |
866 |
|
|
|
867 |
|
|
class MemoryCriticalSection : public wxCriticalSection |
868 |
|
|
{ |
869 |
|
|
public: |
870 |
|
|
MemoryCriticalSection() { |
871 |
|
|
memSectionOk = true; |
872 |
|
|
} |
873 |
|
|
~MemoryCriticalSection() { |
874 |
|
|
memSectionOk = false; |
875 |
|
|
} |
876 |
|
|
}; |
877 |
|
|
|
878 |
|
|
class MemoryCriticalSectionLocker |
879 |
|
|
{ |
880 |
|
|
public: |
881 |
|
|
inline MemoryCriticalSectionLocker(wxCriticalSection& critsect) |
882 |
|
|
: m_critsect(critsect), m_locked(memSectionOk) { if(m_locked) m_critsect.Enter(); } |
883 |
|
|
inline ~MemoryCriticalSectionLocker() { if(m_locked) m_critsect.Leave(); } |
884 |
|
|
|
885 |
|
|
private: |
886 |
|
|
// no assignment operator nor copy ctor |
887 |
|
|
MemoryCriticalSectionLocker(const MemoryCriticalSectionLocker&); |
888 |
|
|
MemoryCriticalSectionLocker& operator=(const MemoryCriticalSectionLocker&); |
889 |
|
|
|
890 |
|
|
wxCriticalSection& m_critsect; |
891 |
|
|
bool m_locked; |
892 |
|
|
}; |
893 |
|
|
|
894 |
|
|
static MemoryCriticalSection memLocker; |
895 |
|
|
|
896 |
|
|
#endif // USE_THREADSAFE_MEMORY_ALLOCATION |
897 |
|
|
|
898 |
|
|
|
899 |
|
|
#ifdef __WXDEBUG__ |
900 |
|
|
#if !(defined(__WXMSW__) && (defined(WXUSINGDLL) || defined(WXMAKINGDLL_BASE))) |
901 |
|
|
#if wxUSE_GLOBAL_MEMORY_OPERATORS |
902 |
|
|
void * operator new (size_t size, wxChar * fileName, int lineNum) |
903 |
|
|
{ |
904 |
|
|
return wxDebugAlloc(size, fileName, lineNum, false, false); |
905 |
|
|
} |
906 |
|
|
|
907 |
|
|
void * operator new (size_t size) |
908 |
|
|
{ |
909 |
|
|
return wxDebugAlloc(size, NULL, 0, false); |
910 |
|
|
} |
911 |
|
|
|
912 |
|
|
void operator delete (void * buf) |
913 |
|
|
{ |
914 |
|
|
wxDebugFree(buf, false); |
915 |
|
|
} |
916 |
|
|
|
917 |
|
|
#if wxUSE_ARRAY_MEMORY_OPERATORS |
918 |
|
|
void * operator new[] (size_t size) |
919 |
|
|
{ |
920 |
|
|
return wxDebugAlloc(size, NULL, 0, false, true); |
921 |
|
|
} |
922 |
|
|
|
923 |
|
|
void * operator new[] (size_t size, wxChar * fileName, int lineNum) |
924 |
|
|
{ |
925 |
|
|
return wxDebugAlloc(size, fileName, lineNum, false, true); |
926 |
|
|
} |
927 |
|
|
|
928 |
|
|
void operator delete[] (void * buf) |
929 |
|
|
{ |
930 |
|
|
wxDebugFree(buf, true); |
931 |
|
|
} |
932 |
|
|
#endif // wxUSE_ARRAY_MEMORY_OPERATORS |
933 |
|
|
#endif // wxUSE_GLOBAL_MEMORY_OPERATORS |
934 |
|
|
#endif // !(defined(__WXMSW__) && (defined(WXUSINGDLL) || defined(WXMAKINGDLL_BASE))) |
935 |
|
|
|
936 |
|
|
// TODO: store whether this is a vector or not. |
937 |
|
|
void * wxDebugAlloc(size_t size, wxChar * fileName, int lineNum, bool isObject, bool WXUNUSED(isVect) ) |
938 |
|
|
{ |
939 |
|
|
#if USE_THREADSAFE_MEMORY_ALLOCATION |
940 |
|
|
MemoryCriticalSectionLocker lock(memLocker); |
941 |
|
|
#endif |
942 |
|
|
|
943 |
|
|
// If not in debugging allocation mode, do the normal thing |
944 |
|
|
// so we don't leave any trace of ourselves in the node list. |
945 |
|
|
|
946 |
|
|
#if defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 ) |
947 |
|
|
// VA 3.0 still has trouble in here |
948 |
|
|
return (void *)malloc(size); |
949 |
|
|
#endif |
950 |
|
|
if (!wxDebugContext::GetDebugMode()) |
951 |
|
|
{ |
952 |
|
|
return (void *)malloc(size); |
953 |
|
|
} |
954 |
|
|
|
955 |
|
|
int totSize = wxDebugContext::TotSize (size); |
956 |
|
|
char * buf = (char *) malloc(totSize); |
957 |
|
|
if (!buf) { |
958 |
|
|
wxLogMessage(wxT("Call to malloc (%ld) failed."), (long)size); |
959 |
|
|
return 0; |
960 |
|
|
} |
961 |
|
|
wxMemStruct * st = (wxMemStruct *)buf; |
962 |
|
|
st->m_firstMarker = MemStartCheck; |
963 |
|
|
st->m_reqSize = size; |
964 |
|
|
st->m_fileName = fileName; |
965 |
|
|
st->m_lineNum = lineNum; |
966 |
|
|
st->m_id = MemStructId; |
967 |
|
|
st->m_prev = 0; |
968 |
|
|
st->m_next = 0; |
969 |
|
|
st->m_isObject = isObject; |
970 |
|
|
|
971 |
|
|
// Errors from Append() shouldn't really happen - but just in case! |
972 |
|
|
if (st->Append () == 0) { |
973 |
|
|
st->ErrorMsg ("Trying to append new node"); |
974 |
|
|
} |
975 |
|
|
|
976 |
|
|
if (wxDebugContext::GetCheckPrevious ()) { |
977 |
|
|
if (st->CheckAllPrevious () < 0) { |
978 |
|
|
st->ErrorMsg ("Checking previous nodes"); |
979 |
|
|
} |
980 |
|
|
} |
981 |
|
|
|
982 |
|
|
// Set up the extra markers at the middle and end. |
983 |
|
|
char * ptr = wxDebugContext::MidMarkerPos (buf); |
984 |
|
|
* (wxMarkerType *) ptr = MemMidCheck; |
985 |
|
|
ptr = wxDebugContext::EndMarkerPos (buf, size); |
986 |
|
|
* (wxMarkerType *) ptr = MemEndCheck; |
987 |
|
|
|
988 |
|
|
// pointer returned points to the start of the caller's |
989 |
|
|
// usable area. |
990 |
|
|
void *m_actualData = (void *) wxDebugContext::CallerMemPos (buf); |
991 |
|
|
st->m_actualData = m_actualData; |
992 |
|
|
|
993 |
|
|
return m_actualData; |
994 |
|
|
} |
995 |
|
|
|
996 |
|
|
// TODO: check whether was allocated as a vector |
997 |
|
|
void wxDebugFree(void * buf, bool WXUNUSED(isVect) ) |
998 |
|
|
{ |
999 |
|
|
#if USE_THREADSAFE_MEMORY_ALLOCATION |
1000 |
|
|
MemoryCriticalSectionLocker lock(memLocker); |
1001 |
|
|
#endif |
1002 |
|
|
|
1003 |
|
|
if (!buf) |
1004 |
|
|
return; |
1005 |
|
|
|
1006 |
|
|
#if defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 ) |
1007 |
|
|
// VA 3.0 still has trouble in here |
1008 |
|
|
free((char *)buf); |
1009 |
|
|
#endif |
1010 |
|
|
// If not in debugging allocation mode, do the normal thing |
1011 |
|
|
// so we don't leave any trace of ourselves in the node list. |
1012 |
|
|
if (!wxDebugContext::GetDebugMode()) |
1013 |
|
|
{ |
1014 |
|
|
free((char *)buf); |
1015 |
|
|
return; |
1016 |
|
|
} |
1017 |
|
|
|
1018 |
|
|
// Points to the start of the entire allocated area. |
1019 |
|
|
char * startPointer = wxDebugContext::StartPos ((char *) buf); |
1020 |
|
|
// Find the struct and make sure that it's identifiable. |
1021 |
|
|
wxMemStruct * st = (wxMemStruct *) wxDebugContext::StructPos (startPointer); |
1022 |
|
|
|
1023 |
|
|
if (! st->ValidateNode ()) |
1024 |
|
|
return; |
1025 |
|
|
|
1026 |
|
|
// If this is the current checkpoint, we need to |
1027 |
|
|
// move the checkpoint back so it points to a valid |
1028 |
|
|
// node. |
1029 |
|
|
if (st == wxDebugContext::checkPoint) |
1030 |
|
|
wxDebugContext::checkPoint = wxDebugContext::checkPoint->m_prev; |
1031 |
|
|
|
1032 |
|
|
if (! st->Unlink ()) |
1033 |
|
|
{ |
1034 |
|
|
st->ErrorMsg ("Unlinking deleted node"); |
1035 |
|
|
} |
1036 |
|
|
|
1037 |
|
|
// Now put in the fill char into the id slot and the caller requested |
1038 |
|
|
// memory locations. |
1039 |
|
|
st->SetDeleted (); |
1040 |
|
|
(void) memset (wxDebugContext::CallerMemPos (startPointer), MemFillChar, |
1041 |
|
|
st->RequestSize ()); |
1042 |
|
|
|
1043 |
|
|
free((char *)st); |
1044 |
|
|
} |
1045 |
|
|
|
1046 |
|
|
#endif // __WXDEBUG__ |
1047 |
|
|
|
1048 |
|
|
// Trace: send output to the current debugging stream |
1049 |
|
|
void wxTrace(const wxChar * ...) |
1050 |
|
|
{ |
1051 |
|
|
#if 1 |
1052 |
|
|
wxFAIL_MSG(wxT("wxTrace is now obsolete. Please use wxDebugXXX instead.")); |
1053 |
|
|
#else |
1054 |
|
|
va_list ap; |
1055 |
|
|
static wxChar buffer[512]; |
1056 |
|
|
|
1057 |
|
|
va_start(ap, fmt); |
1058 |
|
|
|
1059 |
|
|
#ifdef __WXMSW__ |
1060 |
|
|
wvsprintf(buffer,fmt,ap) ; |
1061 |
|
|
#else |
1062 |
|
|
vsprintf(buffer,fmt,ap) ; |
1063 |
|
|
#endif |
1064 |
|
|
|
1065 |
|
|
va_end(ap); |
1066 |
|
|
|
1067 |
|
|
if (wxDebugContext::HasStream()) |
1068 |
|
|
{ |
1069 |
|
|
wxDebugContext::GetStream() << buffer; |
1070 |
|
|
wxDebugContext::GetStream().flush(); |
1071 |
|
|
} |
1072 |
|
|
else |
1073 |
|
|
#ifdef __WXMSW__ |
1074 |
|
|
#ifdef __WIN32__ |
1075 |
|
|
OutputDebugString((LPCTSTR)buffer) ; |
1076 |
|
|
#else |
1077 |
|
|
OutputDebugString((const char*) buffer) ; |
1078 |
|
|
#endif |
1079 |
|
|
#else |
1080 |
|
|
fprintf(stderr, buffer); |
1081 |
|
|
#endif |
1082 |
|
|
#endif |
1083 |
|
|
} |
1084 |
|
|
|
1085 |
|
|
// Trace with level |
1086 |
|
|
void wxTraceLevel(int, const wxChar * ...) |
1087 |
|
|
{ |
1088 |
|
|
#if 1 |
1089 |
|
|
wxFAIL_MSG(wxT("wxTrace is now obsolete. Please use wxDebugXXX instead.")); |
1090 |
|
|
#else |
1091 |
|
|
if (wxDebugContext::GetLevel() < level) |
1092 |
|
|
return; |
1093 |
|
|
|
1094 |
|
|
va_list ap; |
1095 |
|
|
static wxChar buffer[512]; |
1096 |
|
|
|
1097 |
|
|
va_start(ap, fmt); |
1098 |
|
|
|
1099 |
|
|
#ifdef __WXMSW__ |
1100 |
|
|
wxWvsprintf(buffer,fmt,ap) ; |
1101 |
|
|
#else |
1102 |
|
|
vsprintf(buffer,fmt,ap) ; |
1103 |
|
|
#endif |
1104 |
|
|
|
1105 |
|
|
va_end(ap); |
1106 |
|
|
|
1107 |
|
|
if (wxDebugContext::HasStream()) |
1108 |
|
|
{ |
1109 |
|
|
wxDebugContext::GetStream() << buffer; |
1110 |
|
|
wxDebugContext::GetStream().flush(); |
1111 |
|
|
} |
1112 |
|
|
else |
1113 |
|
|
#ifdef __WXMSW__ |
1114 |
|
|
#ifdef __WIN32__ |
1115 |
|
|
OutputDebugString((LPCTSTR)buffer) ; |
1116 |
|
|
#else |
1117 |
|
|
OutputDebugString((const char*) buffer) ; |
1118 |
|
|
#endif |
1119 |
|
|
#else |
1120 |
|
|
fprintf(stderr, buffer); |
1121 |
|
|
#endif |
1122 |
|
|
#endif |
1123 |
|
|
} |
1124 |
|
|
|
1125 |
|
|
//---------------------------------------------------------------------------- |
1126 |
|
|
// Final cleanup after all global objects in all files have been destroyed |
1127 |
|
|
//---------------------------------------------------------------------------- |
1128 |
|
|
|
1129 |
|
|
// Don't set it to 0 by dynamic initialization |
1130 |
|
|
// Some compilers will really do the assignment later |
1131 |
|
|
// All global variables are initialized to 0 at the very beginning, and this is just fine. |
1132 |
|
|
int wxDebugContextDumpDelayCounter::sm_count; |
1133 |
|
|
|
1134 |
|
|
void wxDebugContextDumpDelayCounter::DoDump() |
1135 |
|
|
{ |
1136 |
|
|
if (wxDebugContext::CountObjectsLeft(true) > 0) |
1137 |
|
|
{ |
1138 |
|
|
wxDebugContext::OutputDumpLine(wxT("There were memory leaks.\n")); |
1139 |
|
|
wxDebugContext::Dump(); |
1140 |
|
|
wxDebugContext::PrintStatistics(); |
1141 |
|
|
} |
1142 |
|
|
} |
1143 |
|
|
|
1144 |
|
|
// Even if there is nothing else, make sure that there is at |
1145 |
|
|
// least one cleanup counter object |
1146 |
|
|
static wxDebugContextDumpDelayCounter wxDebugContextDumpDelayCounter_One; |
1147 |
|
|
|
1148 |
|
|
#endif // (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT |