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

Last change on this file since 19 was 14, checked in by umoeller, 25 years ago

Major updates; timers, LVM, miscellaneous.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 24.1 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 */
134
135/* ******************************************************************
136 *
137 * Global variables
138 *
139 ********************************************************************/
140
141#define MEMBLOCKMAGIC_HEAD "\210\203`H&cx$&%\254"
142 // size must be a multiple of 4 or dword-alignment will fail;
143 // there's an extra 0 byte at the end, so we have a size of 12
144 // V0.9.3 (2000-04-17) [umoeller]
145#define MEMBLOCKMAGIC_TAIL "\250\210&%/dfjsk%#,dlhf\223"
146
147HMTX G_hmtxMallocList = NULLHANDLE;
148
149extern PHEAPITEM G_pHeapItemsRoot = NULL;
150PHEAPITEM G_pHeapItemsLast = NULL;
151
152PFNCBMEMDLOG G_pMemdLogFunc = NULL;
153
154extern ULONG G_ulItemsReleased = 0;
155extern ULONG G_ulBytesReleased = 0;
156
157/* ******************************************************************
158 *
159 * Debug heap management
160 *
161 ********************************************************************/
162
163/*
164 *@@ memdLock:
165 * enables the global memory lock to protect
166 * the global variables here. Use memdUnlock
167 * to unlock again, and lock only for the shortest
168 * possible time. This is only used by the memdebug.c
169 * functions.
170 *
171 *@@added V0.9.3 (2000-04-10) [umoeller]
172 */
173
174BOOL memdLock(VOID)
175{
176 APIRET arc = NO_ERROR;
177 if (G_hmtxMallocList == NULLHANDLE)
178 // first call:
179 arc = DosCreateMutexSem(NULL,
180 &G_hmtxMallocList,
181 0, // unshared
182 TRUE); // request now!
183 else
184 arc = DosRequestMutexSem(G_hmtxMallocList,
185 SEM_INDEFINITE_WAIT);
186
187 return (arc == NO_ERROR);
188}
189
190/*
191 *@@ memdUnlock:
192 * the reverse to memdLock.
193 *
194 *@@added V0.9.3 (2000-04-10) [umoeller]
195 */
196
197VOID memdUnlock(VOID)
198{
199 DosReleaseMutexSem(G_hmtxMallocList);
200}
201
202/*
203 *@@ memdMalloc:
204 * wrapper function for malloc() to trace malloc()
205 * calls more precisely.
206 *
207 * If XWorkplace is compiled with debug code, setup.h
208 * automatically #includes memdebug.h, which maps
209 * malloc to this function so that the source file
210 * etc. parameters automatically get passed.
211 *
212 * For each call, we call the default malloc(), whose
213 * return value is returned, and create a HEAPITEM
214 * for remembering the call, which is stored in a global
215 * linked list.
216 *
217 *@@added V0.9.3 (2000-04-11) [umoeller]
218 */
219
220void* memdMalloc(size_t stSize,
221 const char *pcszSourceFile, // in: source file name
222 unsigned long ulLine, // in: source line
223 const char *pcszFunction) // in: function name
224{
225 void *prc = NULL;
226
227 if (stSize == 0)
228 // malloc(0) called: report error
229 if (G_pMemdLogFunc)
230 {
231 CHAR szMsg[1000];
232 sprintf(szMsg,
233 "Function %s (%s, line %d) called malloc(0).",
234 pcszFunction,
235 pcszSourceFile,
236 ulLine);
237 G_pMemdLogFunc(szMsg);
238 }
239
240 if (memdLock())
241 {
242 // call default malloc(), but with the additional
243 // size of our MEMBLOCKMAGIC strings; we'll return
244 // the first byte after the "front" string so we can
245 // check for string overwrites
246 void *pObj = malloc(stSize
247 + sizeof(MEMBLOCKMAGIC_HEAD)
248 + sizeof(MEMBLOCKMAGIC_TAIL));
249 if (pObj)
250 {
251 PHEAPITEM pHeapItem = (PHEAPITEM)malloc(sizeof(HEAPITEM));
252
253 // store "front" magic string
254 memcpy(pObj,
255 MEMBLOCKMAGIC_HEAD,
256 sizeof(MEMBLOCKMAGIC_HEAD));
257 // return address: first byte after "front" magic string
258 prc = ((PBYTE)pObj) + sizeof(MEMBLOCKMAGIC_HEAD);
259 // store "tail" magic string to block which
260 // will be returned plus the size which was requested
261 memcpy(((PBYTE)prc) + stSize,
262 MEMBLOCKMAGIC_TAIL,
263 sizeof(MEMBLOCKMAGIC_TAIL));
264
265 if (pHeapItem)
266 {
267 PTIB ptib;
268 PPIB ppib;
269
270 pHeapItem->pNext = 0;
271
272 pHeapItem->pAfterMagic = prc;
273 pHeapItem->ulSize = stSize;
274 pHeapItem->pcszSourceFile = pcszSourceFile;
275 pHeapItem->ulLine = ulLine;
276 pHeapItem->pcszFunction = pcszFunction;
277
278 DosGetDateTime(&pHeapItem->dtAllocated);
279
280 pHeapItem->ulTID = 0;
281
282 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
283 if (ptib)
284 if (ptib->tib_ptib2)
285 pHeapItem->ulTID = ptib->tib_ptib2->tib2_ultid;
286
287 pHeapItem->fFreed = FALSE;
288
289 // append heap item to linked list
290 if (G_pHeapItemsRoot == NULL)
291 // first item:
292 G_pHeapItemsRoot = pHeapItem;
293 else
294 // we have items already:
295 if (G_pHeapItemsLast)
296 {
297 // last item cached:
298 G_pHeapItemsLast->pNext = pHeapItem;
299 G_pHeapItemsLast = pHeapItem;
300 }
301 else
302 {
303 // not cached: find end of list
304 PHEAPITEM phi = G_pHeapItemsRoot;
305 while (phi->pNext)
306 phi = phi->pNext;
307
308 phi->pNext = pHeapItem;
309 G_pHeapItemsLast = pHeapItem;
310 }
311 }
312 }
313
314 memdUnlock();
315 }
316
317 return (prc);
318}
319
320/*
321 *@@ memdCalloc:
322 * similar to memdMalloc; this is the wrapper for
323 * the calloc() call. This is automatically
324 * remapped also.
325 *
326 *@@added V0.9.3 (2000-04-11) [umoeller]
327 */
328
329void* memdCalloc(size_t num,
330 size_t stSize,
331 const char *pcszSourceFile,
332 unsigned long ulLine,
333 const char *pcszFunction)
334{
335 void *p = memdMalloc(num * stSize,
336 pcszSourceFile,
337 ulLine,
338 pcszFunction);
339 memset(p, 0, num * stSize);
340 return (p);
341}
342
343/*
344 *@@ memdFree:
345 * wrapper for the free() call, which is remapped
346 * by setup.h and memdebug.h like memdMalloc
347 * and memdCalloc. This searches the memory object
348 * (p) which was previously allocated on the linked
349 * list of HEAPITEM's and frees it then by calling
350 * the default free().
351 *
352 * The HEAPITEM itself is not freed, but only marked
353 * as freed. As a result, the linked list can grow
354 * REALLY large. While memdMalloc does not become
355 * slower with large HEAPITEM lists because it only
356 * appends to the end of the list, which is remembered,
357 * memdFree can become extremely slow because the entire
358 * list needs to be searched with each call.
359 * So call memdReleaseFreed from time to time.
360 *
361 *@@added V0.9.3 (2000-04-10) [umoeller]
362 */
363
364void memdFree(void *p,
365 const char *pcszSourceFile,
366 unsigned long ulLine,
367 const char *pcszFunction)
368{
369 BOOL fFound = FALSE;
370 if (memdLock())
371 {
372 // PLISTNODE pNode = lstQueryFirstNode(&G_llHeapItems);
373 PHEAPITEM pHeapItem = G_pHeapItemsRoot;
374
375 // search the list with the pointer which was
376 // really returned by the original malloc(),
377 // that is, the byte before the magic string
378 void *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD);
379
380 while (pHeapItem)
381 {
382 if (pHeapItem->pAfterMagic == p)
383 // the same address may be allocated and freed
384 // several times, so if this address has been
385 // freed, search on
386 if (!pHeapItem->fFreed)
387 {
388 // found:
389 ULONG ulError = 0;
390 // check magic string
391 if (memcmp(pBeforeMagic,
392 MEMBLOCKMAGIC_HEAD,
393 sizeof(MEMBLOCKMAGIC_HEAD))
394 != 0)
395 ulError = 1;
396 else if (memcmp(((PBYTE)pHeapItem->pAfterMagic) + pHeapItem->ulSize,
397 MEMBLOCKMAGIC_TAIL,
398 sizeof(MEMBLOCKMAGIC_TAIL))
399 != 0)
400 ulError = 2;
401
402 if (ulError)
403 {
404 // magic block has been overwritten:
405 if (G_pMemdLogFunc)
406 {
407 CHAR szMsg[1000];
408 sprintf(szMsg,
409 "Magic string %s memory block at 0x%lX has been overwritten.\n"
410 "This was detected by the free() call at %s (%s, line %d).\n"
411 "The block was allocated by %s (%s, line %d).",
412 (ulError == 1) ? "before" : "after",
413 p,
414 pcszFunction,
415 pcszSourceFile,
416 ulLine, // free
417 pHeapItem->pcszFunction,
418 pHeapItem->pcszSourceFile,
419 pHeapItem->ulLine);
420 G_pMemdLogFunc(szMsg);
421 }
422 }
423
424 free(pBeforeMagic);
425 pHeapItem->fFreed = TRUE;
426
427 fFound = TRUE;
428 break;
429 } // if (!pHeapItem->fFreed)
430
431 pHeapItem = pHeapItem->pNext;
432 }
433
434 memdUnlock();
435 }
436
437 if (!fFound)
438 if (G_pMemdLogFunc)
439 {
440 CHAR szMsg[1000];
441 sprintf(szMsg,
442 "free() called with invalid object from %s (%s, line %d) for object 0x%lX.",
443 pcszFunction,
444 pcszSourceFile,
445 ulLine,
446 p);
447 G_pMemdLogFunc(szMsg);
448 }
449}
450
451/*
452 *@@ memdRealloc:
453 * wrapper function for realloc(). See memdMalloc for
454 * details.
455 *
456 *@@added V0.9.6 (2000-11-12) [umoeller]
457 */
458
459void* memdRealloc(void *p,
460 size_t stSize,
461 const char *pcszSourceFile, // in: source file name
462 unsigned long ulLine, // in: source line
463 const char *pcszFunction) // in: function name
464{
465 void *prc = NULL;
466 BOOL fFound = FALSE;
467
468 if (memdLock())
469 {
470 PHEAPITEM pHeapItem = G_pHeapItemsRoot;
471
472 // search the list with the pointer which was
473 // really returned by the original malloc(),
474 // that is, the byte before the magic string
475 void *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD);
476
477 while (pHeapItem)
478 {
479 if (pHeapItem->pAfterMagic == p)
480 // the same address may be allocated and freed
481 // several times, so if this address has been
482 // freed, search on
483 if (!pHeapItem->fFreed)
484 {
485 // found:
486 PVOID pObjNew = 0;
487 ULONG ulError = 0;
488 ULONG cbCopy = 0;
489 PTIB ptib;
490 PPIB ppib;
491
492 // check magic string
493 if (memcmp(pBeforeMagic,
494 MEMBLOCKMAGIC_HEAD,
495 sizeof(MEMBLOCKMAGIC_HEAD))
496 != 0)
497 ulError = 1;
498 else if (memcmp(((PBYTE)pHeapItem->pAfterMagic) + pHeapItem->ulSize,
499 MEMBLOCKMAGIC_TAIL,
500 sizeof(MEMBLOCKMAGIC_TAIL))
501 != 0)
502 ulError = 2;
503
504 if (ulError)
505 {
506 // magic block has been overwritten:
507 if (G_pMemdLogFunc)
508 {
509 CHAR szMsg[1000];
510 sprintf(szMsg,
511 "Magic string %s memory block at 0x%lX has been overwritten.\n"
512 "This was detected by the realloc() call at %s (%s, line %d).\n"
513 "The block was allocated by %s (%s, line %d).",
514 (ulError == 1) ? "before" : "after",
515 p,
516 pcszFunction,
517 pcszSourceFile,
518 ulLine, // free
519 pHeapItem->pcszFunction,
520 pHeapItem->pcszSourceFile,
521 pHeapItem->ulLine);
522 G_pMemdLogFunc(szMsg);
523 }
524 }
525
526 // now reallocate!
527 pObjNew = malloc(stSize // new size
528 + sizeof(MEMBLOCKMAGIC_HEAD)
529 + sizeof(MEMBLOCKMAGIC_TAIL));
530
531 // store "front" magic string
532 memcpy(pObjNew,
533 MEMBLOCKMAGIC_HEAD,
534 sizeof(MEMBLOCKMAGIC_HEAD));
535 // return address: first byte after "front" magic string
536 prc = ((PBYTE)pObjNew) + sizeof(MEMBLOCKMAGIC_HEAD);
537
538 // bytes to copy: the smaller of the old and the new size
539 cbCopy = pHeapItem->ulSize;
540 if (stSize < pHeapItem->ulSize)
541 cbCopy = stSize;
542
543 // copy buffer from old memory object
544 memcpy(prc, // after "front" magic
545 pHeapItem->pAfterMagic,
546 cbCopy);
547
548 // store "tail" magic string to block which
549 // will be returned plus the size which was requested
550 memcpy(((PBYTE)prc) + stSize,
551 MEMBLOCKMAGIC_TAIL,
552 sizeof(MEMBLOCKMAGIC_TAIL));
553
554 // free the old buffer
555 free(pBeforeMagic);
556
557 // update the HEAPITEM
558 pHeapItem->pAfterMagic = prc; // new pointer!
559 pHeapItem->ulSize = stSize; // new size!
560 pHeapItem->pcszSourceFile = pcszSourceFile;
561 pHeapItem->ulLine = ulLine;
562 pHeapItem->pcszFunction = pcszFunction;
563
564 // update date, time, TID
565 DosGetDateTime(&pHeapItem->dtAllocated);
566 pHeapItem->ulTID = 0;
567 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
568 if (ptib)
569 if (ptib->tib_ptib2)
570 pHeapItem->ulTID = ptib->tib_ptib2->tib2_ultid;
571
572 fFound = TRUE;
573 break;
574 } // if (!pHeapItem->fFreed)
575
576 pHeapItem = pHeapItem->pNext;
577 }
578
579 memdUnlock();
580 }
581
582 if (!fFound)
583 if (G_pMemdLogFunc)
584 {
585 CHAR szMsg[1000];
586 sprintf(szMsg,
587 "realloc() called with invalid object from %s (%s, line %d) for object 0x%lX.",
588 pcszFunction,
589 pcszSourceFile,
590 ulLine,
591 p);
592 G_pMemdLogFunc(szMsg);
593 }
594
595 return (prc);
596}
597
598/*
599 *@@ memdReleaseFreed:
600 * goes thru the entire global HEAPITEM's list
601 * and throws out all items which have been freed.
602 * Call this from time to time in order to keep
603 * the system usable. See memdFree() for details.
604 *
605 * Returns the no. of HEAPITEM's that have been
606 * released.
607 *
608 *@@added V0.9.3 (2000-04-11) [umoeller]
609 */
610
611unsigned long memdReleaseFreed(void)
612{
613 BOOL ulItemsReleased = 0,
614 ulBytesReleased = 0;
615 if (memdLock())
616 {
617 PHEAPITEM pHeapItem = G_pHeapItemsRoot,
618 pPrevious = NULL;
619
620 while (pHeapItem)
621 {
622 // store next first, because we can change the "next" pointer
623 PHEAPITEM pNext = pHeapItem->pNext; // can be NULL
624
625 if (pHeapItem->fFreed)
626 {
627 // item was freed:
628 if (pPrevious == NULL)
629 // head of list:
630 G_pHeapItemsRoot = pNext; // can be NULL
631 else
632 // somewhere later:
633 // link next to previous to skip current
634 pPrevious->pNext = pNext; // can be NULL
635
636 ulItemsReleased++;
637 ulBytesReleased += pHeapItem->ulSize;
638
639 if (pHeapItem == G_pHeapItemsLast)
640 // reset "last item" cache
641 G_pHeapItemsLast = NULL;
642
643 free(pHeapItem);
644 }
645 else
646 // item still valid:
647 pPrevious = pHeapItem;
648
649 pHeapItem = pNext;
650 }
651
652 G_ulItemsReleased += ulItemsReleased;
653 G_ulBytesReleased += ulBytesReleased;
654
655 memdUnlock();
656 }
657
658 return (ulItemsReleased);
659}
660
661/* ******************************************************************
662 *
663 * XFolder debugging helpers
664 *
665 ********************************************************************/
666
667#ifdef _PMPRINTF_
668 /*
669 *@@ memdDumpMemoryBlock:
670 * if _PMPRINTF_ has been #define'd before including
671 * memdebug.h,
672 * this will dump a block of memory to the PMPRINTF
673 * output window. Useful for debugging internal
674 * structures.
675 * If _PMPRINTF_ has been NOT #define'd,
676 * no code will be produced at all. :-)
677 */
678
679 void memdDumpMemoryBlock(PBYTE pb, // in: start address
680 ULONG ulSize, // in: size of block
681 ULONG ulIndent) // in: how many spaces to put
682 // before each output line
683 {
684 PSZ psz = strhCreateDump(pb, ulSize, ulIndent);
685 if (psz)
686 {
687 _Pmpf(("\n%s", psz));
688 free(psz);
689 }
690 }
691#endif
692
693#else
694void memdDummy(void)
695{
696 int i = 0;
697}
698#endif
699
Note: See TracBrowser for help on using the repository browser.