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

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

Misc changes

  • 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 {
385 // the same address may be allocated and freed
386 // several times, so if this address has been
387 // freed, search on
388 if (!pHeapItem->fFreed)
389 {
390 // found:
391 ULONG ulError = 0;
392 // check magic string
393 if (memcmp(pBeforeMagic,
394 MEMBLOCKMAGIC_HEAD,
395 sizeof(MEMBLOCKMAGIC_HEAD))
396 != 0)
397 ulError = 1;
398 else if (memcmp(((PBYTE)pHeapItem->pAfterMagic) + pHeapItem->ulSize,
399 MEMBLOCKMAGIC_TAIL,
400 sizeof(MEMBLOCKMAGIC_TAIL))
401 != 0)
402 ulError = 2;
403
404 if (ulError)
405 {
406 // magic block has been overwritten:
407 if (G_pMemdLogFunc)
408 {
409 CHAR szMsg[1000];
410 sprintf(szMsg,
411 "Magic string %s memory block at 0x%lX has been overwritten.\n"
412 "This was detected by the free() call at %s (%s, line %d).\n"
413 "The block was allocated by %s (%s, line %d).",
414 (ulError == 1) ? "before" : "after",
415 p,
416 pcszFunction,
417 pcszSourceFile,
418 ulLine, // free
419 pHeapItem->pcszFunction,
420 pHeapItem->pcszSourceFile,
421 pHeapItem->ulLine);
422 G_pMemdLogFunc(szMsg);
423 }
424 }
425
426 free(pBeforeMagic);
427 pHeapItem->fFreed = TRUE;
428
429 fFound = TRUE;
430 break;
431 } // if (!pHeapItem->fFreed)
432 }
433
434 pHeapItem = pHeapItem->pNext;
435 }
436
437 memdUnlock();
438 }
439
440 if (!fFound)
441 if (G_pMemdLogFunc)
442 {
443 CHAR szMsg[1000];
444 sprintf(szMsg,
445 "free() called with invalid object from %s (%s, line %d) for object 0x%lX.",
446 pcszFunction,
447 pcszSourceFile,
448 ulLine,
449 p);
450 G_pMemdLogFunc(szMsg);
451 }
452}
453
454/*
455 *@@ memdRealloc:
456 * wrapper function for realloc(). See memdMalloc for
457 * details.
458 *
459 *@@added V0.9.6 (2000-11-12) [umoeller]
460 *@@changed V0.9.12 (2001-05-21) [umoeller]: this reported errors on realloc(0), which is a valid call, fixed
461 */
462
463void* memdRealloc(void *p,
464 size_t stSize,
465 const char *pcszSourceFile, // in: source file name
466 unsigned long ulLine, // in: source line
467 const char *pcszFunction) // in: function name
468{
469 void *prc = NULL;
470 BOOL fFound = FALSE;
471
472 if (!p)
473 // p == NULL: this is valid, use malloc() instead
474 // V0.9.12 (2001-05-21) [umoeller]
475 return (memdMalloc(stSize, pcszSourceFile, ulLine, pcszFunction));
476
477 if (memdLock())
478 {
479 PHEAPITEM pHeapItem = G_pHeapItemsRoot;
480
481 // search the list with the pointer which was
482 // really returned by the original malloc(),
483 // that is, the byte before the magic string
484 void *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD);
485
486 while (pHeapItem)
487 {
488 if (pHeapItem->pAfterMagic == p)
489 // the same address may be allocated and freed
490 // several times, so if this address has been
491 // freed, search on
492 if (!pHeapItem->fFreed)
493 {
494 // found:
495 PVOID pObjNew = 0;
496 ULONG ulError = 0;
497 ULONG cbCopy = 0;
498 PTIB ptib;
499 PPIB ppib;
500
501 // check magic string
502 if (memcmp(pBeforeMagic,
503 MEMBLOCKMAGIC_HEAD,
504 sizeof(MEMBLOCKMAGIC_HEAD))
505 != 0)
506 ulError = 1;
507 else if (memcmp(((PBYTE)pHeapItem->pAfterMagic) + pHeapItem->ulSize,
508 MEMBLOCKMAGIC_TAIL,
509 sizeof(MEMBLOCKMAGIC_TAIL))
510 != 0)
511 ulError = 2;
512
513 if (ulError)
514 {
515 // magic block has been overwritten:
516 if (G_pMemdLogFunc)
517 {
518 CHAR szMsg[1000];
519 sprintf(szMsg,
520 "Magic string %s memory block at 0x%lX has been overwritten.\n"
521 "This was detected by the realloc() call at %s (%s, line %d).\n"
522 "The block was allocated by %s (%s, line %d).",
523 (ulError == 1) ? "before" : "after",
524 p,
525 pcszFunction,
526 pcszSourceFile,
527 ulLine, // free
528 pHeapItem->pcszFunction,
529 pHeapItem->pcszSourceFile,
530 pHeapItem->ulLine);
531 G_pMemdLogFunc(szMsg);
532 }
533 }
534
535 // now reallocate!
536 pObjNew = malloc(stSize // new size
537 + sizeof(MEMBLOCKMAGIC_HEAD)
538 + sizeof(MEMBLOCKMAGIC_TAIL));
539
540 // store "front" magic string
541 memcpy(pObjNew,
542 MEMBLOCKMAGIC_HEAD,
543 sizeof(MEMBLOCKMAGIC_HEAD));
544 // return address: first byte after "front" magic string
545 prc = ((PBYTE)pObjNew) + sizeof(MEMBLOCKMAGIC_HEAD);
546
547 // bytes to copy: the smaller of the old and the new size
548 cbCopy = pHeapItem->ulSize;
549 if (stSize < pHeapItem->ulSize)
550 cbCopy = stSize;
551
552 // copy buffer from old memory object
553 memcpy(prc, // after "front" magic
554 pHeapItem->pAfterMagic,
555 cbCopy);
556
557 // store "tail" magic string to block which
558 // will be returned plus the size which was requested
559 memcpy(((PBYTE)prc) + stSize,
560 MEMBLOCKMAGIC_TAIL,
561 sizeof(MEMBLOCKMAGIC_TAIL));
562
563 // free the old buffer
564 free(pBeforeMagic);
565
566 // update the HEAPITEM
567 pHeapItem->pAfterMagic = prc; // new pointer!
568 pHeapItem->ulSize = stSize; // new size!
569 pHeapItem->pcszSourceFile = pcszSourceFile;
570 pHeapItem->ulLine = ulLine;
571 pHeapItem->pcszFunction = pcszFunction;
572
573 // update date, time, TID
574 DosGetDateTime(&pHeapItem->dtAllocated);
575 pHeapItem->ulTID = 0;
576 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
577 if (ptib)
578 if (ptib->tib_ptib2)
579 pHeapItem->ulTID = ptib->tib_ptib2->tib2_ultid;
580
581 fFound = TRUE;
582 break;
583 } // if (!pHeapItem->fFreed)
584
585 pHeapItem = pHeapItem->pNext;
586 }
587
588 memdUnlock();
589 }
590
591 if (!fFound)
592 if (G_pMemdLogFunc)
593 {
594 CHAR szMsg[1000];
595 sprintf(szMsg,
596 "realloc() called with invalid object from %s (%s, line %d) for object 0x%lX.",
597 pcszFunction,
598 pcszSourceFile,
599 ulLine,
600 p);
601 G_pMemdLogFunc(szMsg);
602 }
603
604 return (prc);
605}
606
607/*
608 *@@ memdReleaseFreed:
609 * goes thru the entire global HEAPITEM's list
610 * and throws out all items which have been freed.
611 * Call this from time to time in order to keep
612 * the system usable. See memdFree() for details.
613 *
614 * Returns the no. of HEAPITEM's that have been
615 * released.
616 *
617 *@@added V0.9.3 (2000-04-11) [umoeller]
618 */
619
620unsigned long memdReleaseFreed(void)
621{
622 BOOL ulItemsReleased = 0,
623 ulBytesReleased = 0;
624 if (memdLock())
625 {
626 PHEAPITEM pHeapItem = G_pHeapItemsRoot,
627 pPrevious = NULL;
628
629 while (pHeapItem)
630 {
631 // store next first, because we can change the "next" pointer
632 PHEAPITEM pNext = pHeapItem->pNext; // can be NULL
633
634 if (pHeapItem->fFreed)
635 {
636 // item was freed:
637 if (pPrevious == NULL)
638 // head of list:
639 G_pHeapItemsRoot = pNext; // can be NULL
640 else
641 // somewhere later:
642 // link next to previous to skip current
643 pPrevious->pNext = pNext; // can be NULL
644
645 ulItemsReleased++;
646 ulBytesReleased += pHeapItem->ulSize;
647
648 if (pHeapItem == G_pHeapItemsLast)
649 // reset "last item" cache
650 G_pHeapItemsLast = NULL;
651
652 free(pHeapItem);
653 }
654 else
655 // item still valid:
656 pPrevious = pHeapItem;
657
658 pHeapItem = pNext;
659 }
660
661 G_ulItemsReleased += ulItemsReleased;
662 G_ulBytesReleased += ulBytesReleased;
663
664 memdUnlock();
665 }
666
667 return (ulItemsReleased);
668}
669
670/* ******************************************************************
671 *
672 * XFolder debugging helpers
673 *
674 ********************************************************************/
675
676#ifdef _PMPRINTF_
677 /*
678 *@@ memdDumpMemoryBlock:
679 * if _PMPRINTF_ has been #define'd before including
680 * memdebug.h,
681 * this will dump a block of memory to the PMPRINTF
682 * output window. Useful for debugging internal
683 * structures.
684 * If _PMPRINTF_ has been NOT #define'd,
685 * no code will be produced at all. :-)
686 */
687
688 void memdDumpMemoryBlock(PBYTE pb, // in: start address
689 ULONG ulSize, // in: size of block
690 ULONG ulIndent) // in: how many spaces to put
691 // before each output line
692 {
693 PSZ psz = strhCreateDump(pb, ulSize, ulIndent);
694 if (psz)
695 {
696 _Pmpf(("\n%s", psz));
697 free(psz);
698 }
699 }
700#endif
701
702#else
703void memdDummy(void)
704{
705 int i = 0;
706 i++;
707}
708#endif
709
Note: See TracBrowser for help on using the repository browser.