1 |
|
---|
2 | /*
|
---|
3 | *@@sourcefile memdebug.c:
|
---|
4 | * memory debugging helpers.
|
---|
5 | *
|
---|
6 | * Several things are in here which might turn out to
|
---|
7 | * be useful:
|
---|
8 | *
|
---|
9 | * -- Memory block dumping (memdDumpMemoryBlock).
|
---|
10 | *
|
---|
11 | * -- Sophisticated heap debugging functions, which
|
---|
12 | * automatically replace malloc() and free() etc.
|
---|
13 | * If the __XWPMEMDEBUG__ #define is set before including
|
---|
14 | * memdebug.h, all those standard library calls
|
---|
15 | * are remapped to use the functions in this file
|
---|
16 | * instead.
|
---|
17 | *
|
---|
18 | * At present, malloc(), calloc(), realloc(), strdup()
|
---|
19 | * and free() are supported.
|
---|
20 | *
|
---|
21 | * These log every memory allocation made and log
|
---|
22 | * much more data compared to the VAC++ memory
|
---|
23 | * debugging functions. See HEAPITEM for details.
|
---|
24 | * Automatic heap checking is also included, using
|
---|
25 | * special "magic string" which are checked to
|
---|
26 | * detect overwrites.
|
---|
27 | *
|
---|
28 | * To be able to trace memory errors, set the global
|
---|
29 | * variable G_pMemdLogFunc to a function which can
|
---|
30 | * write an error string to a meaningful place (the
|
---|
31 | * screen or a file). WARNING: While that error function
|
---|
32 | * is executed, the system might be in a global memory
|
---|
33 | * lock, so DON'T display a message box while in that
|
---|
34 | * function, and DO NOT call malloc() or other memory
|
---|
35 | * functions in there.
|
---|
36 | *
|
---|
37 | * These debug functions have been added with V0.9.3
|
---|
38 | * and should now be compiler-independent.
|
---|
39 | *
|
---|
40 | * V0.9.6 added realloc() support and fixed a few bugs.
|
---|
41 | *
|
---|
42 | * With V0.9.16, most of this was rewritten to be much
|
---|
43 | * faster. This no longer slows down the system enormously.
|
---|
44 | *
|
---|
45 | * -- A PM heap debugging window which shows the status
|
---|
46 | * of the heap logging list. See memdCreateMemDebugWindow
|
---|
47 | * (memdebug_win.c) for details.
|
---|
48 | *
|
---|
49 | * To enable memory debugging, do the following in each (!)
|
---|
50 | * of your code modules:
|
---|
51 | *
|
---|
52 | * 1) Include at least <stdlib.h> and <string.h>.
|
---|
53 | *
|
---|
54 | * 2) Include memdebug.h AFTER those two. This will remap
|
---|
55 | * the malloc() etc. calls to the debug functions in
|
---|
56 | * this file by defining macros for them.
|
---|
57 | *
|
---|
58 | * If you don't want those replaced, add
|
---|
59 | + #define DONT_REPLACE_MALLOC
|
---|
60 | * before including memdebug.h.
|
---|
61 | *
|
---|
62 | * To avoid calling a debug function for a single call,
|
---|
63 | * place the malloc call (or whatever) in brackets.
|
---|
64 | *
|
---|
65 | * That's all. XWorkplace's setup.h does this automatically
|
---|
66 | * if XWorkplace is compiled with debug code.
|
---|
67 | *
|
---|
68 | * A couple of WARNINGS:
|
---|
69 | *
|
---|
70 | * 1) When free() is invoked, the memory that was allocated
|
---|
71 | * is freed, but not the memory log entry (the HEAPITEM)
|
---|
72 | * to allow tracing what was freed. As a result, the tree
|
---|
73 | * of memory items keeps growing longer. Do not expect
|
---|
74 | * this to work forever, even though things have greatly
|
---|
75 | * improved with V0.9.16.
|
---|
76 | *
|
---|
77 | * 2) The replacement functions in this file allocate
|
---|
78 | * extra memory for the magic strings. For example, if
|
---|
79 | * you call malloc(100), more than 100 bytes get allocated
|
---|
80 | * to allow for storing the magic strings to detect
|
---|
81 | * memory overwrites. Two magic strings are allocated,
|
---|
82 | * one before the actual buffer, and one behind it.
|
---|
83 | * The pointer returned is _not_ identical to the one
|
---|
84 | * that was internally allocated.
|
---|
85 | *
|
---|
86 | * As a result, YOU MUST NOT confuse the replacement
|
---|
87 | * memory functions with the original ones. If you
|
---|
88 | * use malloc() in one source file and free() the
|
---|
89 | * buffer in another one where debug memory has not
|
---|
90 | * been enabled, you'll get crashes.
|
---|
91 | *
|
---|
92 | * As a rule of thumb, enable memory debugging for all
|
---|
93 | * your source files or for none. And make sure everything
|
---|
94 | * is recompiled when you change your mind.
|
---|
95 | *
|
---|
96 | *@@added V0.9.1 (2000-02-12) [umoeller]
|
---|
97 | */
|
---|
98 |
|
---|
99 | /*
|
---|
100 | * Copyright (C) 2000-2001 Ulrich Mller.
|
---|
101 | * This program is part of the XWorkplace package.
|
---|
102 | * This program is free software; you can redistribute it and/or modify
|
---|
103 | * it under the terms of the GNU General Public License as published by
|
---|
104 | * the Free Software Foundation, in version 2 as it comes in the COPYING
|
---|
105 | * file of the XWorkplace main distribution.
|
---|
106 | * This program is distributed in the hope that it will be useful,
|
---|
107 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
108 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
109 | * GNU General Public License for more details.
|
---|
110 | */
|
---|
111 |
|
---|
112 | #define OS2EMX_PLAIN_CHAR
|
---|
113 | // this is needed for "os2emx.h"; if this is defined,
|
---|
114 | // emx will define PSZ as _signed_ char, otherwise
|
---|
115 | // as unsigned char
|
---|
116 |
|
---|
117 | #define INCL_DOSSEMAPHORES
|
---|
118 | #define INCL_DOSEXCEPTIONS
|
---|
119 | #define INCL_DOSPROCESS
|
---|
120 | #define INCL_DOSERRORS
|
---|
121 |
|
---|
122 | #include <os2.h>
|
---|
123 |
|
---|
124 | #include <stdio.h>
|
---|
125 | #include <string.h>
|
---|
126 | #include <setjmp.h>
|
---|
127 |
|
---|
128 | #include "helpers\tree.h"
|
---|
129 |
|
---|
130 | #define DONT_REPLACE_MALLOC // never do debug memory for this
|
---|
131 | #define MEMDEBUG_PRIVATE
|
---|
132 | #include "setup.h"
|
---|
133 |
|
---|
134 | #ifdef __XWPMEMDEBUG__
|
---|
135 |
|
---|
136 | #include "helpers\dosh.h"
|
---|
137 | #include "helpers\except.h"
|
---|
138 |
|
---|
139 | #include "helpers\memdebug.h" // included by setup.h already
|
---|
140 | #include "helpers\stringh.h"
|
---|
141 |
|
---|
142 | /*
|
---|
143 | *@@category: Helpers\C helpers\Heap debugging
|
---|
144 | * See memdebug.c.
|
---|
145 | */
|
---|
146 |
|
---|
147 | /* ******************************************************************
|
---|
148 | *
|
---|
149 | * Global variables
|
---|
150 | *
|
---|
151 | ********************************************************************/
|
---|
152 |
|
---|
153 | #define MEMBLOCKMAGIC_HEAD "\210\203`H&cx$&%\254"
|
---|
154 | // size must be a multiple of 4 or dword-alignment will fail;
|
---|
155 | // there's an extra 0 byte at the end, so we have a size of 12
|
---|
156 | // V0.9.3 (2000-04-17) [umoeller]
|
---|
157 | #define MEMBLOCKMAGIC_TAIL "\250\210&%/dfjsk%#,dlhf\223"
|
---|
158 |
|
---|
159 | HMTX G_hmtxMallocList = NULLHANDLE;
|
---|
160 |
|
---|
161 | extern TREE *G_pHeapItemsRoot = NULL;
|
---|
162 | extern LONG G_cHeapItems = 0;
|
---|
163 |
|
---|
164 | PFNCBMEMDLOG G_pMemdLogFunc = NULL;
|
---|
165 |
|
---|
166 | extern ULONG G_ulItemsReleased = 0;
|
---|
167 | extern ULONG G_ulBytesReleased = 0;
|
---|
168 |
|
---|
169 | /* ******************************************************************
|
---|
170 | *
|
---|
171 | * Debug heap management
|
---|
172 | *
|
---|
173 | ********************************************************************/
|
---|
174 |
|
---|
175 | /*
|
---|
176 | *@@ memdLock:
|
---|
177 | * enables the global memory lock to protect
|
---|
178 | * the global variables here. Use memdUnlock
|
---|
179 | * to unlock again, and lock only for the shortest
|
---|
180 | * possible time. This is only used by the memdebug.c
|
---|
181 | * functions.
|
---|
182 | *
|
---|
183 | *@@added V0.9.3 (2000-04-10) [umoeller]
|
---|
184 | */
|
---|
185 |
|
---|
186 | BOOL memdLock(VOID)
|
---|
187 | {
|
---|
188 | if (!G_hmtxMallocList)
|
---|
189 | {
|
---|
190 | // first call:
|
---|
191 | if (!DosCreateMutexSem(NULL,
|
---|
192 | &G_hmtxMallocList,
|
---|
193 | 0, // unshared
|
---|
194 | TRUE)) // request now!
|
---|
195 | {
|
---|
196 | treeInit(&G_pHeapItemsRoot, &G_cHeapItems);
|
---|
197 | return TRUE;
|
---|
198 | }
|
---|
199 | }
|
---|
200 | else
|
---|
201 | return !DosRequestMutexSem(G_hmtxMallocList,
|
---|
202 | SEM_INDEFINITE_WAIT);
|
---|
203 |
|
---|
204 | return FALSE;
|
---|
205 | }
|
---|
206 |
|
---|
207 | /*
|
---|
208 | *@@ memdUnlock:
|
---|
209 | * the reverse to memdLock.
|
---|
210 | *
|
---|
211 | *@@added V0.9.3 (2000-04-10) [umoeller]
|
---|
212 | */
|
---|
213 |
|
---|
214 | VOID memdUnlock(VOID)
|
---|
215 | {
|
---|
216 | DosReleaseMutexSem(G_hmtxMallocList);
|
---|
217 | }
|
---|
218 |
|
---|
219 | /*
|
---|
220 | *@@ LogError:
|
---|
221 | *
|
---|
222 | *@@added V0.9.16 (2001-12-08) [umoeller]
|
---|
223 | */
|
---|
224 |
|
---|
225 | STATIC VOID LogError(const char *pcszFormat, // in: format string (like with printf)
|
---|
226 | ...) // in: additional stuff (like with printf)
|
---|
227 | {
|
---|
228 | if (G_pMemdLogFunc)
|
---|
229 | {
|
---|
230 | CHAR szMsg[1000];
|
---|
231 | va_list args;
|
---|
232 |
|
---|
233 | va_start(args, pcszFormat);
|
---|
234 | vsprintf(szMsg, pcszFormat, args);
|
---|
235 | va_end(args);
|
---|
236 | G_pMemdLogFunc(szMsg);
|
---|
237 | }
|
---|
238 | }
|
---|
239 |
|
---|
240 | /*
|
---|
241 | *@@ FindHeapItem:
|
---|
242 | *
|
---|
243 | *@@added V0.9.16 (2001-12-08) [umoeller]
|
---|
244 | */
|
---|
245 |
|
---|
246 | STATIC PHEAPITEM FindHeapItem(void *p)
|
---|
247 | {
|
---|
248 | return (PHEAPITEM)treeFind(G_pHeapItemsRoot,
|
---|
249 | (ULONG)p,
|
---|
250 | treeCompareKeys);
|
---|
251 | }
|
---|
252 |
|
---|
253 | /*
|
---|
254 | *@@ FillHeapItem:
|
---|
255 | *
|
---|
256 | *@@added V0.9.16 (2001-12-08) [umoeller]
|
---|
257 | */
|
---|
258 |
|
---|
259 | STATIC VOID FillHeapItem(PHEAPITEM pHeapItem,
|
---|
260 | void *prc,
|
---|
261 | size_t stSize,
|
---|
262 | const char *pcszSourceFile, // in: source file name
|
---|
263 | unsigned long ulLine, // in: source line
|
---|
264 | const char *pcszFunction) // in: function name
|
---|
265 | {
|
---|
266 | pHeapItem->ulSize = stSize;
|
---|
267 |
|
---|
268 | pHeapItem->pcszSourceFile = pcszSourceFile;
|
---|
269 | pHeapItem->ulLine = ulLine;
|
---|
270 | pHeapItem->pcszFunction = pcszFunction;
|
---|
271 |
|
---|
272 | DosGetDateTime(&pHeapItem->dtAllocated);
|
---|
273 |
|
---|
274 | pHeapItem->ulTID = doshMyTID();
|
---|
275 |
|
---|
276 | pHeapItem->fFreed = FALSE;
|
---|
277 |
|
---|
278 | // use the return pointer as the tree sort key
|
---|
279 | // V0.9.16 (2001-12-08) [umoeller]
|
---|
280 | pHeapItem->Tree.ulKey = (ULONG)prc;
|
---|
281 | }
|
---|
282 |
|
---|
283 | /*
|
---|
284 | *@@ CheckMagics:
|
---|
285 | *
|
---|
286 | *@@added V0.9.16 (2001-12-08) [umoeller]
|
---|
287 | */
|
---|
288 |
|
---|
289 | STATIC VOID CheckMagics(const char *pcszParentFunc,
|
---|
290 | PHEAPITEM pHeapItem,
|
---|
291 | PBYTE p,
|
---|
292 | const char *pcszSourceFile, // in: source file name
|
---|
293 | unsigned long ulLine, // in: source line
|
---|
294 | const char *pcszFunction) // in: function name
|
---|
295 | {
|
---|
296 | void *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD);
|
---|
297 | ULONG ulError = 0;
|
---|
298 |
|
---|
299 | // check magic string
|
---|
300 | if (memcmp(pBeforeMagic,
|
---|
301 | MEMBLOCKMAGIC_HEAD,
|
---|
302 | sizeof(MEMBLOCKMAGIC_HEAD)))
|
---|
303 | ulError = 1;
|
---|
304 | else if (memcmp(((PBYTE)p) + pHeapItem->ulSize,
|
---|
305 | MEMBLOCKMAGIC_TAIL,
|
---|
306 | sizeof(MEMBLOCKMAGIC_TAIL)))
|
---|
307 | ulError = 2;
|
---|
308 |
|
---|
309 | if (ulError)
|
---|
310 | {
|
---|
311 | LogError("%s: Magic string %s memory block at 0x%lX has been overwritten.\n"
|
---|
312 | "This was detected by the free() call at %s (%s, line %d).\n"
|
---|
313 | "The block was allocated by %s (%s, line %d).",
|
---|
314 | pcszParentFunc,
|
---|
315 | (ulError == 1) ? "before" : "after",
|
---|
316 | p,
|
---|
317 | pcszFunction,
|
---|
318 | pcszSourceFile,
|
---|
319 | ulLine, // free
|
---|
320 | pHeapItem->pcszFunction,
|
---|
321 | pHeapItem->pcszSourceFile,
|
---|
322 | pHeapItem->ulLine);
|
---|
323 | }
|
---|
324 | }
|
---|
325 |
|
---|
326 | /*
|
---|
327 | *@@ memdMalloc:
|
---|
328 | * wrapper function for malloc() to trace malloc()
|
---|
329 | * calls more precisely.
|
---|
330 | *
|
---|
331 | * If XWorkplace is compiled with debug code, setup.h
|
---|
332 | * automatically #includes memdebug.h, which maps
|
---|
333 | * malloc to this function so that the source file
|
---|
334 | * etc. parameters automatically get passed.
|
---|
335 | *
|
---|
336 | * For each call, we call the default malloc(), whose
|
---|
337 | * return value is returned, and create a HEAPITEM
|
---|
338 | * for remembering the call, which is stored in a global
|
---|
339 | * linked list.
|
---|
340 | *
|
---|
341 | *@@added V0.9.3 (2000-04-11) [umoeller]
|
---|
342 | *@@changed V0.9.16 (2001-12-08) [umoeller]: reworked to use trees now, much faster
|
---|
343 | */
|
---|
344 |
|
---|
345 | void* memdMalloc(size_t stSize,
|
---|
346 | const char *pcszSourceFile, // in: source file name
|
---|
347 | unsigned long ulLine, // in: source line
|
---|
348 | const char *pcszFunction) // in: function name
|
---|
349 | {
|
---|
350 | void *prc = NULL;
|
---|
351 |
|
---|
352 | if (stSize == 0)
|
---|
353 | // malloc(0) called: report error
|
---|
354 | LogError(__FUNCTION__ ": Function %s (%s, line %d) called malloc(0).",
|
---|
355 | pcszFunction,
|
---|
356 | pcszSourceFile,
|
---|
357 | ulLine);
|
---|
358 | else
|
---|
359 | if (memdLock())
|
---|
360 | {
|
---|
361 | // call default malloc(), but with the additional
|
---|
362 | // size of our MEMBLOCKMAGIC strings; we'll return
|
---|
363 | // the first byte after the "front" string so we can
|
---|
364 | // check for string overwrites
|
---|
365 | void *pObj;
|
---|
366 |
|
---|
367 | if (pObj = malloc( sizeof(MEMBLOCKMAGIC_HEAD)
|
---|
368 | + stSize
|
---|
369 | + sizeof(MEMBLOCKMAGIC_TAIL)))
|
---|
370 | {
|
---|
371 | PHEAPITEM pHeapItem;
|
---|
372 | BOOL fInsert = TRUE;
|
---|
373 |
|
---|
374 | // store "front" magic string
|
---|
375 | memcpy(pObj,
|
---|
376 | MEMBLOCKMAGIC_HEAD,
|
---|
377 | sizeof(MEMBLOCKMAGIC_HEAD));
|
---|
378 | // return address: first byte after "front" magic string
|
---|
379 | prc = ((PBYTE)pObj) + sizeof(MEMBLOCKMAGIC_HEAD);
|
---|
380 | // store "tail" magic string to block which
|
---|
381 | // will be returned plus the size which was requested
|
---|
382 | memcpy(((PBYTE)prc) + stSize,
|
---|
383 | MEMBLOCKMAGIC_TAIL,
|
---|
384 | sizeof(MEMBLOCKMAGIC_TAIL));
|
---|
385 |
|
---|
386 | if (!(pHeapItem = FindHeapItem(prc)))
|
---|
387 | // not re-using old address:
|
---|
388 | // create a new heap item
|
---|
389 | pHeapItem = (PHEAPITEM)malloc(sizeof(HEAPITEM));
|
---|
390 | else
|
---|
391 | fInsert = FALSE;
|
---|
392 |
|
---|
393 | FillHeapItem(pHeapItem,
|
---|
394 | prc,
|
---|
395 | stSize,
|
---|
396 | pcszSourceFile,
|
---|
397 | ulLine,
|
---|
398 | pcszFunction);
|
---|
399 |
|
---|
400 | if (fInsert)
|
---|
401 | // append heap item to linked list
|
---|
402 | if (treeInsert(&G_pHeapItemsRoot,
|
---|
403 | &G_cHeapItems,
|
---|
404 | (TREE*)pHeapItem,
|
---|
405 | treeCompareKeys))
|
---|
406 | {
|
---|
407 | LogError(__FUNCTION__ ": treeInsert failed for memory block at 0x%lX.\n"
|
---|
408 | "The block was allocated by %s (%s, line %d).",
|
---|
409 | prc,
|
---|
410 | pcszFunction,
|
---|
411 | pcszSourceFile,
|
---|
412 | ulLine);
|
---|
413 | }
|
---|
414 | }
|
---|
415 |
|
---|
416 | memdUnlock();
|
---|
417 | }
|
---|
418 |
|
---|
419 | return prc;
|
---|
420 | }
|
---|
421 |
|
---|
422 | /*
|
---|
423 | *@@ memdCalloc:
|
---|
424 | * similar to memdMalloc; this is the wrapper for
|
---|
425 | * the calloc() call. This is automatically
|
---|
426 | * remapped also.
|
---|
427 | *
|
---|
428 | *@@added V0.9.3 (2000-04-11) [umoeller]
|
---|
429 | */
|
---|
430 |
|
---|
431 | void* memdCalloc(size_t num,
|
---|
432 | size_t stSize,
|
---|
433 | const char *pcszSourceFile,
|
---|
434 | unsigned long ulLine,
|
---|
435 | const char *pcszFunction)
|
---|
436 | {
|
---|
437 | void *p = memdMalloc(num * stSize,
|
---|
438 | pcszSourceFile,
|
---|
439 | ulLine,
|
---|
440 | pcszFunction);
|
---|
441 | memset(p, 0, num * stSize);
|
---|
442 | return p;
|
---|
443 | }
|
---|
444 |
|
---|
445 | /*
|
---|
446 | *@@ memdFree:
|
---|
447 | * wrapper for the free() call, which is remapped
|
---|
448 | * by setup.h and memdebug.h like memdMalloc
|
---|
449 | * and memdCalloc. This searches the memory object
|
---|
450 | * (p) which was previously allocated on the linked
|
---|
451 | * list of HEAPITEM's and frees it then by calling
|
---|
452 | * the default free().
|
---|
453 | *
|
---|
454 | * The HEAPITEM itself is not freed, but only marked
|
---|
455 | * as freed. As a result, the linked list can grow
|
---|
456 | * REALLY large. While memdMalloc does not become
|
---|
457 | * slower with large HEAPITEM lists because it only
|
---|
458 | * appends to the end of the list, which is remembered,
|
---|
459 | * memdFree can become extremely slow because the entire
|
---|
460 | * list needs to be searched with each call.
|
---|
461 | * So call memdReleaseFreed from time to time.
|
---|
462 | *
|
---|
463 | *@@added V0.9.3 (2000-04-10) [umoeller]
|
---|
464 | *@@changed V0.9.16 (2001-12-08) [umoeller]: reworked to use trees now, much faster
|
---|
465 | */
|
---|
466 |
|
---|
467 | void memdFree(void *p,
|
---|
468 | const char *pcszSourceFile,
|
---|
469 | unsigned long ulLine,
|
---|
470 | const char *pcszFunction)
|
---|
471 | {
|
---|
472 | if (memdLock())
|
---|
473 | {
|
---|
474 | PHEAPITEM pHeapItem;
|
---|
475 |
|
---|
476 | // search the list with the pointer which was
|
---|
477 | // really returned by the original malloc(),
|
---|
478 | // that is, the byte after the magic string
|
---|
479 | if (pHeapItem = FindHeapItem(p))
|
---|
480 | {
|
---|
481 | // the same address may be allocated and freed
|
---|
482 | // several times, so check
|
---|
483 | if (!pHeapItem->fFreed)
|
---|
484 | {
|
---|
485 | // found:
|
---|
486 | void *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD);
|
---|
487 |
|
---|
488 | CheckMagics(__FUNCTION__,
|
---|
489 | pHeapItem,
|
---|
490 | (PBYTE)p,
|
---|
491 | pcszSourceFile,
|
---|
492 | ulLine,
|
---|
493 | pcszFunction);
|
---|
494 |
|
---|
495 | // free the real memory item
|
---|
496 | free(pBeforeMagic);
|
---|
497 |
|
---|
498 | // mark the heap item as freed, but
|
---|
499 | // keep it in the list
|
---|
500 | pHeapItem->fFreed = TRUE;
|
---|
501 |
|
---|
502 | } // if (!pHeapItem->fFreed)
|
---|
503 | else
|
---|
504 | // memory block has been freed twice:
|
---|
505 | LogError(__FUNCTION__ ": Memory block at 0x%lX has been freed twice.\n"
|
---|
506 | "This was detected by the free() call at %s (%s, line %d).\n"
|
---|
507 | "The block was originally allocated by %s (%s, line %d).",
|
---|
508 | p,
|
---|
509 | pcszFunction,
|
---|
510 | pcszSourceFile,
|
---|
511 | ulLine, // free
|
---|
512 | pHeapItem->pcszFunction,
|
---|
513 | pHeapItem->pcszSourceFile,
|
---|
514 | pHeapItem->ulLine);
|
---|
515 | }
|
---|
516 | else
|
---|
517 | // not found:
|
---|
518 | LogError(__FUNCTION__ ": free() called with invalid object 0x%lX from %s (%s, line %d).",
|
---|
519 | p,
|
---|
520 | pcszFunction,
|
---|
521 | pcszSourceFile,
|
---|
522 | ulLine);
|
---|
523 |
|
---|
524 | memdUnlock();
|
---|
525 | }
|
---|
526 | }
|
---|
527 |
|
---|
528 | /*
|
---|
529 | *@@ memdRealloc:
|
---|
530 | * wrapper function for realloc(). See memdMalloc for
|
---|
531 | * details.
|
---|
532 | *
|
---|
533 | *@@added V0.9.6 (2000-11-12) [umoeller]
|
---|
534 | *@@changed V0.9.12 (2001-05-21) [umoeller]: this reported errors on realloc(0), which is a valid call, fixed
|
---|
535 | *@@changed V0.9.16 (2001-12-08) [umoeller]: reworked to use trees now, much faster
|
---|
536 | */
|
---|
537 |
|
---|
538 | void* memdRealloc(void *p,
|
---|
539 | size_t stSize,
|
---|
540 | const char *pcszSourceFile, // in: source file name
|
---|
541 | unsigned long ulLine, // in: source line
|
---|
542 | const char *pcszFunction) // in: function name
|
---|
543 | {
|
---|
544 | void *prc = NULL;
|
---|
545 |
|
---|
546 | if (!p)
|
---|
547 | // p == NULL: this is valid, use malloc() instead
|
---|
548 | // V0.9.12 (2001-05-21) [umoeller]
|
---|
549 | return memdMalloc(stSize, pcszSourceFile, ulLine, pcszFunction);
|
---|
550 |
|
---|
551 | if (memdLock())
|
---|
552 | {
|
---|
553 | // search the list with the pointer which was
|
---|
554 | // really returned by the original malloc(),
|
---|
555 | // that is, the byte after the magic string
|
---|
556 | PHEAPITEM pHeapItem, pExisting;
|
---|
557 | if (pHeapItem = FindHeapItem(p))
|
---|
558 | {
|
---|
559 | // found:
|
---|
560 | if (pHeapItem->fFreed)
|
---|
561 | {
|
---|
562 | LogError(__FUNCTION__ ": realloc() called with memory block at 0x%lX that was already freed.\n"
|
---|
563 | "This was detected by the realloc() call at %s (%s, line %d).\n"
|
---|
564 | "The block was originally allocated by %s (%s, line %d).",
|
---|
565 | p,
|
---|
566 | pcszFunction,
|
---|
567 | pcszSourceFile,
|
---|
568 | ulLine, // free
|
---|
569 | pHeapItem->pcszFunction,
|
---|
570 | pHeapItem->pcszSourceFile,
|
---|
571 | pHeapItem->ulLine);
|
---|
572 | }
|
---|
573 | else
|
---|
574 | {
|
---|
575 | // block is valid:
|
---|
576 | void *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD);
|
---|
577 | PVOID pObjNew = 0;
|
---|
578 | ULONG ulError = 0;
|
---|
579 | ULONG cbCopy = 0;
|
---|
580 |
|
---|
581 | CheckMagics(__FUNCTION__,
|
---|
582 | pHeapItem,
|
---|
583 | (PBYTE)p,
|
---|
584 | pcszSourceFile,
|
---|
585 | ulLine,
|
---|
586 | pcszFunction);
|
---|
587 |
|
---|
588 | // now reallocate!
|
---|
589 | pObjNew = malloc( sizeof(MEMBLOCKMAGIC_HEAD)
|
---|
590 | + stSize // new size
|
---|
591 | + sizeof(MEMBLOCKMAGIC_TAIL));
|
---|
592 |
|
---|
593 | // store "front" magic string
|
---|
594 | memcpy(pObjNew,
|
---|
595 | MEMBLOCKMAGIC_HEAD,
|
---|
596 | sizeof(MEMBLOCKMAGIC_HEAD));
|
---|
597 | // return address: first byte after "front" magic string
|
---|
598 | prc = ((PBYTE)pObjNew) + sizeof(MEMBLOCKMAGIC_HEAD);
|
---|
599 |
|
---|
600 | // bytes to copy: the smaller of the old and the new size
|
---|
601 | cbCopy = pHeapItem->ulSize;
|
---|
602 | if (stSize < pHeapItem->ulSize)
|
---|
603 | cbCopy = stSize;
|
---|
604 |
|
---|
605 | // copy buffer from old memory object
|
---|
606 | memcpy(prc, // after "front" magic
|
---|
607 | p,
|
---|
608 | cbCopy);
|
---|
609 |
|
---|
610 | // store "tail" magic string to block which
|
---|
611 | // will be returned plus the size which was requested
|
---|
612 | memcpy(((PBYTE)prc) + stSize,
|
---|
613 | MEMBLOCKMAGIC_TAIL,
|
---|
614 | sizeof(MEMBLOCKMAGIC_TAIL));
|
---|
615 |
|
---|
616 | // free the old buffer
|
---|
617 | free(pBeforeMagic);
|
---|
618 |
|
---|
619 | // update the tree, since prc has changed
|
---|
620 | treeDelete(&G_pHeapItemsRoot,
|
---|
621 | &G_cHeapItems,
|
---|
622 | (TREE*)pHeapItem);
|
---|
623 | // append heap item to linked list
|
---|
624 | if (pExisting = FindHeapItem(prc))
|
---|
625 | {
|
---|
626 | // a different heap item exists for this address:
|
---|
627 | // delete this one and use that instead; there's
|
---|
628 | // no need to re-insert either
|
---|
629 | free(pHeapItem);
|
---|
630 | pHeapItem = pExisting;
|
---|
631 | }
|
---|
632 |
|
---|
633 | FillHeapItem(pHeapItem,
|
---|
634 | prc,
|
---|
635 | stSize,
|
---|
636 | pcszSourceFile,
|
---|
637 | ulLine,
|
---|
638 | pcszFunction);
|
---|
639 |
|
---|
640 | // insert only if we didn't use an existing item
|
---|
641 | if (!pExisting)
|
---|
642 | if (treeInsert(&G_pHeapItemsRoot,
|
---|
643 | &G_cHeapItems,
|
---|
644 | (TREE*)pHeapItem,
|
---|
645 | treeCompareKeys))
|
---|
646 | {
|
---|
647 | LogError(__FUNCTION__ ": treeInsert failed for memory block at 0x%lX.\n"
|
---|
648 | "The block was allocated by %s (%s, line %d).",
|
---|
649 | prc,
|
---|
650 | pcszFunction,
|
---|
651 | pcszSourceFile,
|
---|
652 | ulLine);
|
---|
653 | }
|
---|
654 |
|
---|
655 | } // if (!pHeapItem->fFreed)
|
---|
656 | }
|
---|
657 | else
|
---|
658 | LogError(__FUNCTION__ ": realloc() called with invalid object from %s (%s, line %d) for object 0x%lX.",
|
---|
659 | pcszFunction,
|
---|
660 | pcszSourceFile,
|
---|
661 | ulLine,
|
---|
662 | p);
|
---|
663 |
|
---|
664 | memdUnlock();
|
---|
665 | }
|
---|
666 |
|
---|
667 | return prc;
|
---|
668 | }
|
---|
669 |
|
---|
670 | /*
|
---|
671 | *@@ memdReleaseFreed:
|
---|
672 | * goes thru the entire global HEAPITEM's list
|
---|
673 | * and throws out all items which have been freed.
|
---|
674 | * Call this from time to time in order to keep
|
---|
675 | * the system usable. See memdFree() for details.
|
---|
676 | *
|
---|
677 | * Returns the no. of HEAPITEM's that have been
|
---|
678 | * released.
|
---|
679 | *
|
---|
680 | *@@added V0.9.3 (2000-04-11) [umoeller]
|
---|
681 | */
|
---|
682 |
|
---|
683 | unsigned long memdReleaseFreed(void)
|
---|
684 | {
|
---|
685 | BOOL ulItemsReleased = 0,
|
---|
686 | ulBytesReleased = 0;
|
---|
687 | if (memdLock())
|
---|
688 | {
|
---|
689 | /* PHEAPITEM pHeapItem = treeFirst(G_pHeapItemsRoot);
|
---|
690 |
|
---|
691 | while (pHeapItem)
|
---|
692 | {
|
---|
693 | // store next first, because we can change the "next" pointer
|
---|
694 | PHEAPITEM pNext = treeNext(pHeapItem);
|
---|
695 |
|
---|
696 | if (pHeapItem->fFreed)
|
---|
697 | {
|
---|
698 | // item was freed:
|
---|
699 | if (pPrevious == NULL)
|
---|
700 | // head of list:
|
---|
701 | G_pHeapItemsRoot = pNext; // can be NULL
|
---|
702 | else
|
---|
703 | // somewhere later:
|
---|
704 | // link next to previous to skip current
|
---|
705 | pPrevious->pNext = pNext; // can be NULL
|
---|
706 |
|
---|
707 | ulItemsReleased++;
|
---|
708 | ulBytesReleased += pHeapItem->ulSize;
|
---|
709 |
|
---|
710 | if (pHeapItem == G_pHeapItemsLast)
|
---|
711 | // reset "last item" cache
|
---|
712 | G_pHeapItemsLast = NULL;
|
---|
713 |
|
---|
714 | free(pHeapItem);
|
---|
715 | }
|
---|
716 | else
|
---|
717 | // item still valid:
|
---|
718 | pPrevious = pHeapItem;
|
---|
719 |
|
---|
720 | pHeapItem = pNext;
|
---|
721 | }
|
---|
722 | */
|
---|
723 | G_ulItemsReleased += ulItemsReleased;
|
---|
724 | G_ulBytesReleased += ulBytesReleased;
|
---|
725 |
|
---|
726 | memdUnlock();
|
---|
727 | }
|
---|
728 |
|
---|
729 | return ulItemsReleased;
|
---|
730 | }
|
---|
731 |
|
---|
732 | /* ******************************************************************
|
---|
733 | *
|
---|
734 | * XFolder debugging helpers
|
---|
735 | *
|
---|
736 | ********************************************************************/
|
---|
737 |
|
---|
738 | #ifdef _PMPRINTF_
|
---|
739 | /*
|
---|
740 | *@@ memdDumpMemoryBlock:
|
---|
741 | * if _PMPRINTF_ has been #define'd before including
|
---|
742 | * memdebug.h,
|
---|
743 | * this will dump a block of memory to the PMPRINTF
|
---|
744 | * output window. Useful for debugging internal
|
---|
745 | * structures.
|
---|
746 | * If _PMPRINTF_ has been NOT #define'd,
|
---|
747 | * no code will be produced at all. :-)
|
---|
748 | */
|
---|
749 |
|
---|
750 | /*
|
---|
751 | void memdDumpMemoryBlock(PBYTE pb, // in: start address
|
---|
752 | ULONG ulSize, // in: size of block
|
---|
753 | ULONG ulIndent) // in: how many spaces to put
|
---|
754 | // before each output line
|
---|
755 | {
|
---|
756 | TRY_QUIET(excpt1)
|
---|
757 | {
|
---|
758 | PSZ psz;
|
---|
759 | if (psz = strhCreateDump(pb, ulSize, ulIndent))
|
---|
760 | {
|
---|
761 | _Pmpf(("\n%s", psz));
|
---|
762 | free(psz);
|
---|
763 | }
|
---|
764 | }
|
---|
765 | CATCH(excpt1)
|
---|
766 | {
|
---|
767 | _Pmpf(("Crash in " __FUNCTION__ ));
|
---|
768 | } END_CATCH();
|
---|
769 | }
|
---|
770 | */
|
---|
771 | #endif
|
---|
772 |
|
---|
773 | #endif
|
---|
774 |
|
---|