source: trunk/src/helpers/memdebug.c@ 75

Last change on this file since 75 was 71, checked in by umoeller, 24 years ago

misc updates

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