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

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

Initial checkin of helpers code which used to be in WarpIN.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 43.7 KB
Line 
1
2/*
3 * memdebug.c:
4 * memory debugging helpers. Memory debugging is enabled
5 * if the __XWPMEMDEBUG__ #define is set in setup.h.
6 *
7 * Several things are in here which might turn out to
8 * be useful:
9 *
10 * -- Memory block dumping (memdDumpMemoryBlock).
11 *
12 * -- Sophisticated heap debugging functions, which
13 * automatically replace malloc() and free() etc.
14 * when XWorkplace is compiled with debug code.
15 * These log every memory allocation made and log
16 * much more data compared to the VAC++ memory
17 * debugging functions. See HEAPITEM for details.
18 * Automatic heap checking is also included, using
19 * special "magic string" which are checked to
20 * detect overwrites.
21 *
22 * To be able to trace memory errors, set the global
23 * variable G_pMemdLogFunc to a function which can
24 * write an error string to a meaningful place (the
25 * screen or a file). WARNING: While that error function
26 * is executed, the system might be in a global memory
27 * lock, so DON'T display a message box while in that
28 * function.
29 *
30 * At present, malloc(), calloc(), strdup() and free()
31 * are supported. If you invoke free() on a memory block
32 * allocated by a function other than the above, you'll
33 * get a runtime error.
34 *
35 * These debug functions have been added with V0.9.3
36 * and should now be compiler-independent.
37 *
38 * -- A PM heap debugging window which shows the statuzs
39 * of the heap logging list. See memdCreateMemDebugWindow
40 * for details.
41 *
42 * To enable memory debugging, do the following:
43 *
44 * 1) Include at least <stdlib.h> and <string.h>.
45 *
46 * 2) Include memdebug.h AFTER those two. This will remap
47 * the malloc() etc. calls.
48 *
49 * If you don't want those replaced, add
50 + #define DONT_REPLACE_MALLOC
51 * before including memdebug.h.
52 *
53 * That's all. XWorkplace's setup.h does this automatically
54 * if XWorkplace is compiled with debug code.
55 *
56 *@@added V0.9.1 (2000-02-12) [umoeller]
57 */
58
59/*
60 * Copyright (C) 2000 Ulrich M”ller.
61 * This program is part of the XWorkplace package.
62 * This program is free software; you can redistribute it and/or modify
63 * it under the terms of the GNU General Public License as published by
64 * the Free Software Foundation, in version 2 as it comes in the COPYING
65 * file of the XWorkplace main distribution.
66 * This program is distributed in the hope that it will be useful,
67 * but WITHOUT ANY WARRANTY; without even the implied warranty of
68 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
69 * GNU General Public License for more details.
70 */
71
72#define OS2EMX_PLAIN_CHAR
73 // this is needed for "os2emx.h"; if this is defined,
74 // emx will define PSZ as _signed_ char, otherwise
75 // as unsigned char
76
77#define INCL_DOSSEMAPHORES
78#define INCL_DOSEXCEPTIONS
79#define INCL_DOSPROCESS
80#define INCL_DOSERRORS
81
82#define INCL_WINWINDOWMGR
83#define INCL_WINFRAMEMGR
84#define INCL_WINCOUNTRY
85#define INCL_WINSYS
86#define INCL_WINMENUS
87#define INCL_WINSTDCNR
88#include <os2.h>
89
90#include <stdio.h>
91#include <string.h>
92// #include <malloc.h>
93#include <setjmp.h>
94
95#define DONT_REPLACE_MALLOC // we need the "real" malloc in this file
96#include "setup.h"
97
98#ifdef __XWPMEMDEBUG__
99
100#include "helpers\cnrh.h"
101#include "helpers\except.h"
102// #include "helpers\memdebug.h" // included by setup.h already
103#include "helpers\stringh.h"
104#include "helpers\winh.h"
105
106/*
107 *@@category: Helpers\C helpers\Heap debugging
108 */
109
110/* ******************************************************************
111 * *
112 * Global variables *
113 * *
114 ********************************************************************/
115
116#define MEMBLOCKMAGIC_HEAD "\210\203`H&cx$&%\254"
117 // size must be a multiple of 4 or dword-alignment will fail;
118 // there's an extra 0 byte at the end, so we have a size of 12
119 // V0.9.3 (2000-04-17) [umoeller]
120#define MEMBLOCKMAGIC_TAIL "\250\210&%/dfjsk%#,dlhf\223"
121
122/*
123 *@@ HEAPITEM:
124 * informational structure created for each
125 * malloc() call by memdMalloc. These are stored
126 * in a global linked list (G_pHeapItemsRoot).
127 *
128 * We cannot use the linklist.c functions for
129 * managing the linked list because these use
130 * malloc in turn, which would lead to infinite
131 * loops.
132 *
133 *@@added V0.9.3 (2000-04-11) [umoeller]
134 */
135
136typedef struct _HEAPITEM
137{
138 struct _HEAPITEM *pNext; // next item in linked list or NULL if last
139
140 void *pAfterMagic; // memory pointer returned by memdMalloc;
141 // this points to after the magic string
142 unsigned long ulSize; // size of *pData
143
144 const char *pcszSourceFile; // as passed to memdMalloc
145 unsigned long ulLine; // as passed to memdMalloc
146 const char *pcszFunction; // as passed to memdMalloc
147
148 DATETIME dtAllocated; // system date/time at time of memdMalloc call
149
150 ULONG ulTID; // thread ID that memdMalloc was running on
151
152 BOOL fFreed; // TRUE only after item has been freed by memdFree
153} HEAPITEM, *PHEAPITEM;
154
155HMTX G_hmtxMallocList = NULLHANDLE;
156
157PHEAPITEM G_pHeapItemsRoot = NULL,
158 G_pHeapItemsLast = NULL;
159
160PSZ G_pszMemCnrTitle = NULL;
161
162PFNCBMEMDLOG G_pMemdLogFunc = NULL;
163
164ULONG G_ulItemsReleased = 0,
165 G_ulBytesReleased = 0;
166
167/* ******************************************************************
168 * *
169 * Debug heap management *
170 * *
171 ********************************************************************/
172
173/*
174 *@@ memdLock:
175 * enables the global memory lock to protect
176 * the global variables here. Use memdUnlock
177 * to unlock again, and lock only for the shortest
178 * possible time. This is only used by the memdebug.c
179 * functions.
180 *
181 *@@added V0.9.3 (2000-04-10) [umoeller]
182 */
183
184BOOL memdLock(VOID)
185{
186 APIRET arc = NO_ERROR;
187 if (G_hmtxMallocList == NULLHANDLE)
188 // first call:
189 arc = DosCreateMutexSem(NULL,
190 &G_hmtxMallocList,
191 0,
192 FALSE);
193
194 arc = DosRequestMutexSem(G_hmtxMallocList,
195 SEM_INDEFINITE_WAIT);
196
197 return (arc == NO_ERROR);
198}
199
200/*
201 *@@ memdUnlock:
202 * the reverse to memdLock.
203 *
204 *@@added V0.9.3 (2000-04-10) [umoeller]
205 */
206
207VOID memdUnlock(VOID)
208{
209 DosReleaseMutexSem(G_hmtxMallocList);
210}
211
212/*
213 *@@ memdMalloc:
214 * wrapper function for malloc() to trace malloc()
215 * calls more precisely.
216 *
217 * If XWorkplace is compiled with debug code, setup.h
218 * automatically #includes memdebug.h, which maps
219 * malloc to this function so that the source file
220 * etc. parameters automatically get passed.
221 *
222 * For each call, we call the default malloc(), whose
223 * return value is returned, and create a HEAPITEM
224 * for remembering the call, which is stored in a global
225 * linked list.
226 *
227 *@@added V0.9.3 (2000-04-11) [umoeller]
228 */
229
230void* memdMalloc(size_t stSize,
231 const char *pcszSourceFile, // in: source file name
232 unsigned long ulLine, // in: source line
233 const char *pcszFunction) // in: function name
234{
235 void *prc = NULL;
236
237 if (stSize == 0)
238 // malloc(0) called: report error
239 if (G_pMemdLogFunc)
240 {
241 CHAR szMsg[1000];
242 sprintf(szMsg,
243 "Function %s (%s, line %d) called malloc(0).",
244 pcszFunction,
245 pcszSourceFile,
246 ulLine);
247 G_pMemdLogFunc(szMsg);
248 }
249
250 if (memdLock())
251 {
252 // call default malloc(), but with the additional
253 // size of our MEMBLOCKMAGIC strings; we'll return
254 // the first byte after the "front" string so we can
255 // check for string overwrites
256 void *pObj = malloc(stSize
257 + sizeof(MEMBLOCKMAGIC_HEAD)
258 + sizeof(MEMBLOCKMAGIC_TAIL));
259 if (pObj)
260 {
261 PHEAPITEM pHeapItem = (PHEAPITEM)malloc(sizeof(HEAPITEM));
262
263 // store "front" magic string
264 memcpy(pObj,
265 MEMBLOCKMAGIC_HEAD,
266 sizeof(MEMBLOCKMAGIC_HEAD));
267 // return address: first byte after "front" magic string
268 prc = ((PBYTE)pObj) + sizeof(MEMBLOCKMAGIC_HEAD);
269 // store "tail" magic string to block which
270 // will be returned plus the size which was requested
271 memcpy(((PBYTE)prc) + stSize,
272 MEMBLOCKMAGIC_TAIL,
273 sizeof(MEMBLOCKMAGIC_TAIL));
274
275 if (pHeapItem)
276 {
277 PTIB ptib;
278 PPIB ppib;
279
280 pHeapItem->pNext = 0;
281
282 pHeapItem->pAfterMagic = prc;
283 pHeapItem->ulSize = stSize;
284 pHeapItem->pcszSourceFile = pcszSourceFile;
285 pHeapItem->ulLine = ulLine;
286 pHeapItem->pcszFunction = pcszFunction;
287
288 DosGetDateTime(&pHeapItem->dtAllocated);
289
290 pHeapItem->ulTID = 0;
291
292 if (DosGetInfoBlocks(&ptib, &ppib) == NO_ERROR)
293 if (ptib)
294 if (ptib->tib_ptib2)
295 pHeapItem->ulTID = ptib->tib_ptib2->tib2_ultid;
296
297 pHeapItem->fFreed = FALSE;
298
299 // append heap item to linked list
300 if (G_pHeapItemsRoot == NULL)
301 // first item:
302 G_pHeapItemsRoot = pHeapItem;
303 else
304 // we have items already:
305 if (G_pHeapItemsLast)
306 {
307 // last item cached:
308 G_pHeapItemsLast->pNext = pHeapItem;
309 G_pHeapItemsLast = pHeapItem;
310 }
311 else
312 {
313 // not cached: find end of list
314 PHEAPITEM phi = G_pHeapItemsRoot;
315 while (phi->pNext)
316 phi = phi->pNext;
317
318 phi->pNext = pHeapItem;
319 G_pHeapItemsLast = pHeapItem;
320 }
321 }
322 }
323
324 memdUnlock();
325 }
326
327 return (prc);
328}
329
330/*
331 *@@ memdCalloc:
332 * similar to memdMalloc; this is the wrapper for
333 * the calloc() call. This is automatically
334 * remapped also.
335 *
336 *@@added V0.9.3 (2000-04-11) [umoeller]
337 */
338
339void* memdCalloc(size_t num,
340 size_t stSize,
341 const char *pcszSourceFile,
342 unsigned long ulLine,
343 const char *pcszFunction)
344{
345 void *p = memdMalloc(num * stSize,
346 pcszSourceFile,
347 ulLine,
348 pcszFunction);
349 memset(p, 0, num * stSize);
350 return (p);
351}
352
353/*
354 *@@ memdFree:
355 * wrapper for the free() call, which is remapped
356 * by setup.h and memdebug.h like memdMalloc
357 * and memdCalloc. This searches the memory object
358 * (p) which was previously allocated on the linked
359 * list of HEAPITEM's and frees it then by calling
360 * the default free().
361 *
362 * The HEAPITEM itself is not freed, but only marked
363 * as freed. As a result, the linked list can grow
364 * REALLY large. While memdMalloc does not become
365 * slower with large HEAPITEM lists because it only
366 * appends to the end of the list, which is remembered,
367 * memdFree can become extremely slow because the entire
368 * list needs to be searched with each call.
369 * So call memdReleaseFreed from time to time.
370 *
371 *@@added V0.9.3 (2000-04-10) [umoeller]
372 */
373
374void memdFree(void *p,
375 const char *pcszSourceFile,
376 unsigned long ulLine,
377 const char *pcszFunction)
378{
379 BOOL fFound = FALSE;
380 if (memdLock())
381 {
382 // PLISTNODE pNode = lstQueryFirstNode(&G_llHeapItems);
383 PHEAPITEM pHeapItem = G_pHeapItemsRoot;
384
385 // search the list with the pointer which was
386 // really returned by the original malloc(),
387 // that is, the byte before the magic string
388 void *pBeforeMagic = ((PBYTE)p) - sizeof(MEMBLOCKMAGIC_HEAD);
389
390 while (pHeapItem)
391 {
392 if (pHeapItem->pAfterMagic == p)
393 // the same address may be allocated and freed
394 // several times, so if this address has been
395 // freed, search on
396 if (!pHeapItem->fFreed)
397 {
398 // found:
399 ULONG ulError = 0;
400 // check magic string
401 if (memcmp(pBeforeMagic,
402 MEMBLOCKMAGIC_HEAD,
403 sizeof(MEMBLOCKMAGIC_HEAD))
404 != 0)
405 ulError = 1;
406 else if (memcmp(((PBYTE)pHeapItem->pAfterMagic) + pHeapItem->ulSize,
407 MEMBLOCKMAGIC_TAIL,
408 sizeof(MEMBLOCKMAGIC_TAIL))
409 != 0)
410 ulError = 2;
411
412 if (ulError)
413 {
414 // magic block has been overwritten:
415 if (G_pMemdLogFunc)
416 {
417 CHAR szMsg[1000];
418 sprintf(szMsg,
419 "Magic string %s memory block at 0x%lX has been overwritten.\n"
420 "This was detected by the free() call at %s (%s, line %d).\n"
421 "The block was allocated by %s (%s, line %d).",
422 (ulError == 1) ? "before" : "after",
423 p,
424 pcszFunction,
425 pcszSourceFile,
426 ulLine, // free
427 pHeapItem->pcszFunction,
428 pHeapItem->pcszSourceFile,
429 pHeapItem->ulLine);
430 G_pMemdLogFunc(szMsg);
431 }
432 }
433
434 free(pBeforeMagic);
435 pHeapItem->fFreed = TRUE;
436
437 /* lstRemoveNode(&G_llHeapItems,
438 pNode); */
439 fFound = TRUE;
440 break;
441 }
442
443 pHeapItem = pHeapItem->pNext;
444 }
445
446 memdUnlock();
447 }
448
449 if (!fFound)
450 if (G_pMemdLogFunc)
451 {
452 CHAR szMsg[1000];
453 sprintf(szMsg,
454 "free() failed. Called from %s (%s, line %d) for object 0x%lX.",
455 pcszFunction,
456 pcszSourceFile,
457 ulLine,
458 p);
459 G_pMemdLogFunc(szMsg);
460 }
461}
462
463/*
464 *@@ memdReleaseFreed:
465 * goes thru the entire global HEAPITEM's list
466 * and throws out all items which have been freed.
467 * Call this from time to time in order to keep
468 * the system usable. See memdFree() for details.
469 *
470 * Returns the no. of HEAPITEM's that have been
471 * released.
472 *
473 *@@added V0.9.3 (2000-04-11) [umoeller]
474 */
475
476unsigned long memdReleaseFreed(void)
477{
478 BOOL ulItemsReleased = 0,
479 ulBytesReleased = 0;
480 if (memdLock())
481 {
482 PHEAPITEM pHeapItem = G_pHeapItemsRoot,
483 pPrevious = NULL;
484
485 while (pHeapItem)
486 {
487 PHEAPITEM pNext = pHeapItem->pNext; // can be NULL
488 if (pHeapItem->fFreed)
489 {
490 // item freed:
491 if (pPrevious == NULL)
492 // head of list:
493 G_pHeapItemsRoot = pNext; // can be NULL
494 else
495 // somewhere later:
496 // link next to previous to skip current
497 pPrevious->pNext = pNext; // can be NULL
498
499 ulItemsReleased++;
500 ulBytesReleased += pHeapItem->ulSize;
501
502 free(pHeapItem);
503 }
504 else
505 // item still valid:
506 pPrevious = pHeapItem;
507
508 pHeapItem = pNext;
509 }
510
511 G_ulItemsReleased += ulItemsReleased;
512 G_ulBytesReleased += ulBytesReleased;
513
514 memdUnlock();
515 }
516
517 return (ulItemsReleased);
518}
519
520/* ******************************************************************
521 * *
522 * XFolder debugging helpers *
523 * *
524 ********************************************************************/
525
526#ifdef _PMPRINTF_
527 /*
528 *@@ memdDumpMemoryBlock:
529 * if _PMPRINTF_ has been #define'd before including
530 * memdebug.h,
531 * this will dump a block of memory to the PMPRINTF
532 * output window. Useful for debugging internal
533 * structures.
534 * If _PMPRINTF_ has been NOT #define'd,
535 * no code will be produced at all. :-)
536 */
537
538 void memdDumpMemoryBlock(PBYTE pb, // in: start address
539 ULONG ulSize, // in: size of block
540 ULONG ulIndent) // in: how many spaces to put
541 // before each output line
542 {
543 PSZ psz = strhCreateDump(pb, ulSize, ulIndent);
544 if (psz)
545 {
546 _Pmpf(("\n%s", psz));
547 free(psz);
548 }
549 }
550#endif
551
552/* ******************************************************************
553 * *
554 * Heap debugging window *
555 * *
556 ********************************************************************/
557
558/*
559 *@@ MEMRECORD:
560 *
561 *@@added V0.9.1 (99-12-04) [umoeller]
562 */
563
564typedef struct _MEMRECORD
565{
566 RECORDCORE recc;
567
568 ULONG ulIndex;
569
570 CDATE cdateAllocated;
571 CTIME ctimeAllocated;
572
573 PSZ pszFreed;
574
575 ULONG ulTID;
576
577 PSZ pszFunction; // points to szFunction
578 CHAR szFunction[400];
579
580 PSZ pszSource; // points to szSource
581 CHAR szSource[CCHMAXPATH];
582
583 ULONG ulLine;
584
585 PSZ pszAddress; // points to szAddress
586 CHAR szAddress[20];
587
588 ULONG ulSize;
589
590} MEMRECORD, *PMEMRECORD;
591
592/* ULONG ulHeapItemsCount1,
593 ulHeapItemsCount2;
594ULONG ulTotalAllocated,
595 ulTotalFreed;
596PMEMRECORD pMemRecordThis = NULL;
597PSZ pszMemCnrTitle = NULL; */
598
599#if 0
600 /*
601 *@@ fncbMemHeapWalkCount:
602 * callback func for _heap_walk function used for
603 * fnwpMemDebug.
604 *
605 *@@added V0.9.1 (99-12-04) [umoeller]
606 */
607
608 int fncbMemHeapWalkCount(const void *pObject,
609 size_t Size,
610 int useflag,
611 int status,
612 const char *filename,
613 size_t line)
614 {
615 // skip all the items which seem to be
616 // internal to the runtime
617 if ((filename) || (useflag == _FREEENTRY))
618 {
619 ulHeapItemsCount1++;
620 if (useflag == _FREEENTRY)
621 ulTotalFreed += Size;
622 else
623 ulTotalAllocated += Size;
624 }
625 return (0);
626 }
627
628 /*
629 *@@ fncbMemHeapWalkFill:
630 * callback func for _heap_walk function used for
631 * fnwpMemDebug.
632 *
633 *@@added V0.9.1 (99-12-04) [umoeller]
634 */
635
636 int fncbMemHeapWalkFill(const void *pObject,
637 size_t Size,
638 int useflag,
639 int status,
640 const char *filename,
641 size_t line)
642 {
643 // skip all the items which seem to be
644 // internal to the runtime
645 if ((filename) || (useflag == _FREEENTRY))
646 {
647 ulHeapItemsCount2++;
648 if ((pMemRecordThis) && (ulHeapItemsCount2 < ulHeapItemsCount1))
649 {
650 pMemRecordThis->ulIndex = ulHeapItemsCount2 - 1;
651
652 pMemRecordThis->pObject = pObject;
653 pMemRecordThis->useflag = useflag;
654 pMemRecordThis->status = status;
655 pMemRecordThis->filename = filename;
656
657 pMemRecordThis->pszAddress = pMemRecordThis->szAddress;
658
659 pMemRecordThis->ulSize = Size;
660
661 pMemRecordThis->pszSource = pMemRecordThis->szSource;
662
663 pMemRecordThis->ulLine = line;
664
665 pMemRecordThis = (PMEMRECORD)pMemRecordThis->recc.preccNextRecord;
666 }
667 else
668 return (1); // stop
669 }
670
671 return (0);
672 }
673
674 /*
675 *@@ memdCreateRecordsVAC:
676 *
677 *@@added V0.9.3 (2000-04-10) [umoeller]
678 */
679
680 VOID memdCreateRecordsVAC(HWND hwndCnr)
681 {
682 // count heap items
683 ulHeapItemsCount1 = 0;
684 ulTotalFreed = 0;
685 ulTotalAllocated = 0;
686 _heap_walk(fncbMemHeapWalkCount);
687
688 pMemRecordFirst = (PMEMRECORD)cnrhAllocRecords(hwndCnr,
689 sizeof(MEMRECORD),
690 ulHeapItemsCount1);
691 if (pMemRecordFirst)
692 {
693 ulHeapItemsCount2 = 0;
694 pMemRecordThis = pMemRecordFirst;
695 _heap_walk(fncbMemHeapWalkFill);
696
697 // the following doesn't work while _heap_walk is running
698 pMemRecordThis = pMemRecordFirst;
699 while (pMemRecordThis)
700 {
701 switch (pMemRecordThis->useflag)
702 {
703 case _USEDENTRY: pMemRecordThis->pszUseFlag = "Used"; break;
704 case _FREEENTRY: pMemRecordThis->pszUseFlag = "Freed"; break;
705 }
706
707 switch (pMemRecordThis->status)
708 {
709 case _HEAPBADBEGIN: pMemRecordThis->pszStatus = "heap bad begin"; break;
710 case _HEAPBADNODE: pMemRecordThis->pszStatus = "heap bad node"; break;
711 case _HEAPEMPTY: pMemRecordThis->pszStatus = "heap empty"; break;
712 case _HEAPOK: pMemRecordThis->pszStatus = "OK"; break;
713 }
714
715 sprintf(pMemRecordThis->szAddress, "0x%lX", pMemRecordThis->pObject);
716 strcpy(pMemRecordThis->szSource,
717 (pMemRecordThis->filename)
718 ? pMemRecordThis->filename
719 : "?");
720
721 pMemRecordThis = (PMEMRECORD)pMemRecordThis->recc.preccNextRecord;
722 }
723
724 cnrhInsertRecords(hwndCnr,
725 NULL, // parent
726 (PRECORDCORE)pMemRecordFirst,
727 TRUE,
728 NULL,
729 CRA_RECORDREADONLY,
730 ulHeapItemsCount2);
731 }
732 }
733
734#endif
735
736/*
737 *@@ memdCreateRecords:
738 *
739 *@@added V0.9.3 (2000-04-10) [umoeller]
740 */
741
742VOID memdCreateRecords(HWND hwndCnr,
743 PULONG pulTotalItems,
744 PULONG pulAllocatedItems,
745 PULONG pulFreedItems,
746 PULONG pulTotalBytes,
747 PULONG pulAllocatedBytes,
748 PULONG pulFreedBytes)
749{
750 // count heap items
751 ULONG ulHeapItemsCount1 = 0;
752 PMEMRECORD pMemRecordFirst;
753
754 if (memdLock())
755 {
756 // PLISTNODE pNode = lstQueryFirstNode(&G_llHeapItems);
757 PHEAPITEM pHeapItem = G_pHeapItemsRoot;
758
759 *pulTotalItems = 0;
760 *pulAllocatedItems = 0;
761 *pulFreedItems = 0;
762
763 *pulTotalBytes = 0;
764 *pulAllocatedBytes = 0;
765 *pulFreedBytes = 0;
766
767 while (pHeapItem)
768 {
769 ulHeapItemsCount1++;
770 if (pHeapItem->fFreed)
771 {
772 (*pulFreedItems)++;
773 (*pulFreedBytes) += pHeapItem->ulSize;
774 }
775 else
776 {
777 (*pulAllocatedItems)++;
778 (*pulAllocatedBytes) += pHeapItem->ulSize;
779 }
780
781 (*pulTotalBytes) += pHeapItem->ulSize;
782
783 pHeapItem = pHeapItem->pNext;
784 }
785
786 *pulTotalItems = ulHeapItemsCount1;
787
788 pMemRecordFirst = (PMEMRECORD)cnrhAllocRecords(hwndCnr,
789 sizeof(MEMRECORD),
790 ulHeapItemsCount1);
791 if (pMemRecordFirst)
792 {
793 ULONG ulHeapItemsCount2 = 0;
794 PMEMRECORD pMemRecordThis = pMemRecordFirst;
795 pHeapItem = G_pHeapItemsRoot;
796 // PLISTNODE pMemNode = lstQueryFirstNode(&G_llHeapItems);
797
798 while ((pMemRecordThis) && (pHeapItem))
799 {
800 // PHEAPITEM pHeapItem = (PHEAPITEM)pMemNode->pItemData;
801
802 pMemRecordThis->ulIndex = ulHeapItemsCount2++;
803
804 cnrhDateTimeDos2Win(&pHeapItem->dtAllocated,
805 &pMemRecordThis->cdateAllocated,
806 &pMemRecordThis->ctimeAllocated);
807
808 if (pHeapItem->fFreed)
809 pMemRecordThis->pszFreed = "yes";
810
811 pMemRecordThis->ulTID = pHeapItem->ulTID;
812
813 strcpy(pMemRecordThis->szSource, pHeapItem->pcszSourceFile);
814 pMemRecordThis->pszSource = pMemRecordThis->szSource;
815
816 pMemRecordThis->ulLine = pHeapItem->ulLine;
817
818 strcpy(pMemRecordThis->szFunction, pHeapItem->pcszFunction);
819 pMemRecordThis->pszFunction = pMemRecordThis->szFunction;
820
821 sprintf(pMemRecordThis->szAddress, "0x%lX", pHeapItem->pAfterMagic);
822 pMemRecordThis->pszAddress = pMemRecordThis->szAddress;
823
824 pMemRecordThis->ulSize = pHeapItem->ulSize;
825
826
827 /* switch (pMemRecordThis->useflag)
828 {
829 case _USEDENTRY: pMemRecordThis->pszUseFlag = "Used"; break;
830 case _FREEENTRY: pMemRecordThis->pszUseFlag = "Freed"; break;
831 }
832
833 switch (pMemRecordThis->status)
834 {
835 case _HEAPBADBEGIN: pMemRecordThis->pszStatus = "heap bad begin"; break;
836 case _HEAPBADNODE: pMemRecordThis->pszStatus = "heap bad node"; break;
837 case _HEAPEMPTY: pMemRecordThis->pszStatus = "heap empty"; break;
838 case _HEAPOK: pMemRecordThis->pszStatus = "OK"; break;
839 }
840
841 sprintf(pMemRecordThis->szAddress, "0x%lX", pMemRecordThis->pObject);
842 strcpy(pMemRecordThis->szSource,
843 (pMemRecordThis->filename)
844 ? pMemRecordThis->filename
845 : "?"); */
846
847 pMemRecordThis = (PMEMRECORD)pMemRecordThis->recc.preccNextRecord;
848 pHeapItem = pHeapItem->pNext;
849 }
850
851 cnrhInsertRecords(hwndCnr,
852 NULL, // parent
853 (PRECORDCORE)pMemRecordFirst,
854 TRUE,
855 NULL,
856 CRA_RECORDREADONLY,
857 ulHeapItemsCount2);
858 }
859
860 memdUnlock();
861 }
862}
863
864/*
865 *@@ mnu_fnCompareIndex:
866 *
867 *@@added V0.9.1 (99-12-03) [umoeller]
868 */
869
870SHORT EXPENTRY mnu_fnCompareIndex(PMEMRECORD pmrc1, PMEMRECORD pmrc2, PVOID pStorage)
871{
872 // HAB habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);
873 pStorage = pStorage; // to keep the compiler happy
874 if ((pmrc1) && (pmrc2))
875 if (pmrc1->ulIndex < pmrc2->ulIndex)
876 return (-1);
877 else if (pmrc1->ulIndex > pmrc2->ulIndex)
878 return (1);
879
880 return (0);
881}
882
883/*
884 *@@ mnu_fnCompareSourceFile:
885 *
886 *@@added V0.9.1 (99-12-03) [umoeller]
887 */
888
889SHORT EXPENTRY mnu_fnCompareSourceFile(PMEMRECORD pmrc1, PMEMRECORD pmrc2, PVOID pStorage)
890{
891 HAB habDesktop = WinQueryAnchorBlock(HWND_DESKTOP);
892 pStorage = pStorage; // to keep the compiler happy
893 if ((pmrc1) && (pmrc2))
894 switch (WinCompareStrings(habDesktop, 0, 0,
895 pmrc1->szSource,
896 pmrc2->szSource,
897 0))
898 {
899 case WCS_LT: return (-1);
900 case WCS_GT: return (1);
901 default: // equal
902 if (pmrc1->ulLine < pmrc2->ulLine)
903 return (-1);
904 else if (pmrc1->ulLine > pmrc2->ulLine)
905 return (1);
906
907 }
908
909 return (0);
910}
911
912#define ID_MEMCNR 1000
913
914/*
915 *@@ memd_fnwpMemDebug:
916 * client window proc for the heap debugger window
917 * accessible from the Desktop context menu if
918 * __XWPMEMDEBUG__ is defined. Otherwise, this is not
919 * compiled.
920 *
921 * Usage: this is a regular PM client window procedure
922 * to be used with WinRegisterClass and WinCreateStdWindow.
923 * See dtpMenuItemSelected, which uses this.
924 *
925 * This creates a container with all the memory objects
926 * with the size of the client area in turn.
927 *
928 *@@added V0.9.1 (99-12-04) [umoeller]
929 */
930
931
932MRESULT EXPENTRY memd_fnwpMemDebug(HWND hwndClient, ULONG msg, MPARAM mp1, MPARAM mp2)
933{
934 MRESULT mrc = 0;
935
936 switch (msg)
937 {
938 case WM_CREATE:
939 {
940 TRY_LOUD(excpt1, NULL)
941 {
942 // PCREATESTRUCT pcs = (PCREATESTRUCT)mp2;
943 HWND hwndCnr;
944 hwndCnr = WinCreateWindow(hwndClient, // parent
945 WC_CONTAINER,
946 "",
947 WS_VISIBLE | CCS_MINIICONS | CCS_READONLY | CCS_SINGLESEL,
948 0, 0, 0, 0,
949 hwndClient, // owner
950 HWND_TOP,
951 ID_MEMCNR,
952 NULL, NULL);
953 if (hwndCnr)
954 {
955 XFIELDINFO xfi[11];
956 PFIELDINFO pfi = NULL;
957 PMEMRECORD pMemRecordFirst;
958 int i = 0;
959
960 ULONG ulTotalItems = 0,
961 ulAllocatedItems = 0,
962 ulFreedItems = 0;
963 ULONG ulTotalBytes = 0,
964 ulAllocatedBytes = 0,
965 ulFreedBytes = 0;
966
967 // set up cnr details view
968 xfi[i].ulFieldOffset = FIELDOFFSET(MEMRECORD, ulIndex);
969 xfi[i].pszColumnTitle = "No.";
970 xfi[i].ulDataType = CFA_ULONG;
971 xfi[i++].ulOrientation = CFA_RIGHT;
972
973 xfi[i].ulFieldOffset = FIELDOFFSET(MEMRECORD, cdateAllocated);
974 xfi[i].pszColumnTitle = "Date";
975 xfi[i].ulDataType = CFA_DATE;
976 xfi[i++].ulOrientation = CFA_LEFT;
977
978 xfi[i].ulFieldOffset = FIELDOFFSET(MEMRECORD, ctimeAllocated);
979 xfi[i].pszColumnTitle = "Time";
980 xfi[i].ulDataType = CFA_TIME;
981 xfi[i++].ulOrientation = CFA_LEFT;
982
983 xfi[i].ulFieldOffset = FIELDOFFSET(MEMRECORD, pszFreed);
984 xfi[i].pszColumnTitle = "Freed";
985 xfi[i].ulDataType = CFA_STRING;
986 xfi[i++].ulOrientation = CFA_CENTER;
987
988 xfi[i].ulFieldOffset = FIELDOFFSET(MEMRECORD, ulTID);
989 xfi[i].pszColumnTitle = "TID";
990 xfi[i].ulDataType = CFA_ULONG;
991 xfi[i++].ulOrientation = CFA_RIGHT;
992
993 xfi[i].ulFieldOffset = FIELDOFFSET(MEMRECORD, pszFunction);
994 xfi[i].pszColumnTitle = "Function";
995 xfi[i].ulDataType = CFA_STRING;
996 xfi[i++].ulOrientation = CFA_LEFT;
997
998 xfi[i].ulFieldOffset = FIELDOFFSET(MEMRECORD, pszSource);
999 xfi[i].pszColumnTitle = "Source";
1000 xfi[i].ulDataType = CFA_STRING;
1001 xfi[i++].ulOrientation = CFA_LEFT;
1002
1003 xfi[i].ulFieldOffset = FIELDOFFSET(MEMRECORD, ulLine);
1004 xfi[i].pszColumnTitle = "Line";
1005 xfi[i].ulDataType = CFA_ULONG;
1006 xfi[i++].ulOrientation = CFA_RIGHT;
1007
1008 xfi[i].ulFieldOffset = FIELDOFFSET(MEMRECORD, ulSize);
1009 xfi[i].pszColumnTitle = "Size";
1010 xfi[i].ulDataType = CFA_ULONG;
1011 xfi[i++].ulOrientation = CFA_RIGHT;
1012
1013 xfi[i].ulFieldOffset = FIELDOFFSET(MEMRECORD, pszAddress);
1014 xfi[i].pszColumnTitle = "Address";
1015 xfi[i].ulDataType = CFA_STRING;
1016 xfi[i++].ulOrientation = CFA_LEFT;
1017
1018 pfi = cnrhSetFieldInfos(hwndCnr,
1019 &xfi[0],
1020 i, // array item count
1021 TRUE, // no draw lines
1022 3); // return column
1023
1024 {
1025 PSZ pszFont = "9.WarpSans";
1026 WinSetPresParam(hwndCnr,
1027 PP_FONTNAMESIZE,
1028 strlen(pszFont),
1029 pszFont);
1030 }
1031
1032 memdCreateRecords(hwndCnr,
1033 &ulTotalItems,
1034 &ulAllocatedItems,
1035 &ulFreedItems,
1036 &ulTotalBytes,
1037 &ulAllocatedBytes,
1038 &ulFreedBytes);
1039
1040 BEGIN_CNRINFO()
1041 {
1042 CHAR szCnrTitle[1000];
1043 CHAR szTotalItems[100],
1044 szAllocatedItems[100],
1045 szFreedItems[100],
1046 szReleasedItems[100];
1047 CHAR szTotalBytes[100],
1048 szAllocatedBytes[100],
1049 szFreedBytes[100],
1050 szReleasedBytes[100];
1051 sprintf(szCnrTitle,
1052 "Total logs in use: %s items = %s bytes\n"
1053 " Total in use: %s items = %s bytes\n"
1054 " Total freed: %s items = %s bytes\n"
1055 "Total logs released: %s items = %s bytes",
1056 strhThousandsDouble(szTotalItems,
1057 ulTotalItems,
1058 '.'),
1059 strhThousandsDouble(szTotalBytes,
1060 ulTotalBytes,
1061 '.'),
1062 strhThousandsDouble(szAllocatedItems,
1063 ulAllocatedItems,
1064 '.'),
1065 strhThousandsDouble(szAllocatedBytes,
1066 ulAllocatedBytes,
1067 '.'),
1068 strhThousandsDouble(szFreedItems,
1069 ulFreedItems,
1070 '.'),
1071 strhThousandsDouble(szFreedBytes,
1072 ulFreedBytes,
1073 '.'),
1074 strhThousandsDouble(szReleasedItems,
1075 G_ulItemsReleased,
1076 '.'),
1077 strhThousandsDouble(szReleasedBytes,
1078 G_ulBytesReleased,
1079 '.'));
1080 G_pszMemCnrTitle = strdup(szCnrTitle);
1081 cnrhSetTitle(G_pszMemCnrTitle);
1082 cnrhSetView(CV_DETAIL | CV_MINI | CA_DETAILSVIEWTITLES
1083 | CA_DRAWICON
1084 | CA_CONTAINERTITLE | CA_TITLEREADONLY
1085 | CA_TITLESEPARATOR | CA_TITLELEFT);
1086 cnrhSetSplitBarAfter(pfi);
1087 cnrhSetSplitBarPos(250);
1088 } END_CNRINFO(hwndCnr);
1089
1090 WinSetFocus(HWND_DESKTOP, hwndCnr);
1091 }
1092 }
1093 CATCH(excpt1) {} END_CATCH();
1094
1095 mrc = WinDefWindowProc(hwndClient, msg, mp1, mp2);
1096 break; }
1097
1098 case WM_WINDOWPOSCHANGED:
1099 {
1100 PSWP pswp = (PSWP)mp1;
1101 mrc = WinDefWindowProc(hwndClient, msg, mp1, mp2);
1102 if (pswp->fl & SWP_SIZE)
1103 {
1104 WinSetWindowPos(WinWindowFromID(hwndClient, ID_MEMCNR), // cnr
1105 HWND_TOP,
1106 0, 0, pswp->cx, pswp->cy,
1107 SWP_SIZE | SWP_MOVE | SWP_SHOW);
1108 }
1109 break; }
1110
1111 case WM_CONTROL:
1112 {
1113 USHORT usItemID = SHORT1FROMMP(mp1),
1114 usNotifyCode = SHORT2FROMMP(mp1);
1115 if (usItemID == ID_MEMCNR) // cnr
1116 {
1117 switch (usNotifyCode)
1118 {
1119 case CN_CONTEXTMENU:
1120 {
1121 PMEMRECORD precc = (PMEMRECORD)mp2;
1122 if (precc == NULL)
1123 {
1124 // whitespace
1125 HWND hwndMenu = WinCreateMenu(HWND_DESKTOP,
1126 NULL); // no menu template
1127 winhInsertMenuItem(hwndMenu,
1128 MIT_END,
1129 1001,
1130 "Sort by index",
1131 MIS_TEXT, 0);
1132 winhInsertMenuItem(hwndMenu,
1133 MIT_END,
1134 1002,
1135 "Sort by source file",
1136 MIS_TEXT, 0);
1137 cnrhShowContextMenu(WinWindowFromID(hwndClient, ID_MEMCNR),
1138 NULL, // record
1139 hwndMenu,
1140 hwndClient);
1141 }
1142 }
1143 }
1144 }
1145 break; }
1146
1147 case WM_COMMAND:
1148 switch (SHORT1FROMMP(mp1))
1149 {
1150 case 1001: // sort by index
1151 WinSendMsg(WinWindowFromID(hwndClient, ID_MEMCNR),
1152 CM_SORTRECORD,
1153 (MPARAM)mnu_fnCompareIndex,
1154 0);
1155 break;
1156
1157 case 1002: // sort by source file
1158 WinSendMsg(WinWindowFromID(hwndClient, ID_MEMCNR),
1159 CM_SORTRECORD,
1160 (MPARAM)mnu_fnCompareSourceFile,
1161 0);
1162 break;
1163 }
1164 break;
1165
1166 case WM_CLOSE:
1167 WinDestroyWindow(WinWindowFromID(hwndClient, ID_MEMCNR));
1168 WinDestroyWindow(WinQueryWindow(hwndClient, QW_PARENT));
1169 free(G_pszMemCnrTitle);
1170 G_pszMemCnrTitle = NULL;
1171 break;
1172
1173 default:
1174 mrc = WinDefWindowProc(hwndClient, msg, mp1, mp2);
1175 }
1176
1177 return (mrc);
1178}
1179
1180/*
1181 *@@ memdCreateMemDebugWindow:
1182 * creates a heap debugging window which
1183 * is a standard frame with a container,
1184 * listing all heap objects ever allocated.
1185 *
1186 * The client of this standard frame is in
1187 * memd_fnwpMemDebug.
1188 *
1189 * This thing lists all calls to malloc()
1190 * which were ever made, including the
1191 * source file and source line number which
1192 * made the call. Extreeeemely useful for
1193 * detecting memory leaks.
1194 *
1195 * This only works if the memory functions
1196 * have been replaced with the debug versions
1197 * in this file.
1198 */
1199
1200VOID memdCreateMemDebugWindow(VOID)
1201{
1202 ULONG flStyle = FCF_TITLEBAR | FCF_SYSMENU | FCF_HIDEMAX
1203 | FCF_SIZEBORDER | FCF_SHELLPOSITION
1204 | FCF_NOBYTEALIGN | FCF_TASKLIST;
1205 if (WinRegisterClass(WinQueryAnchorBlock(HWND_DESKTOP),
1206 "XWPMemDebug",
1207 memd_fnwpMemDebug, 0L, 0))
1208 {
1209 HWND hwndClient;
1210 HWND hwndMemFrame = WinCreateStdWindow(HWND_DESKTOP,
1211 0L,
1212 &flStyle,
1213 "XWPMemDebug",
1214 "Allocated XWorkplace Memory Objects",
1215 0L,
1216 NULLHANDLE, // resource
1217 0,
1218 &hwndClient);
1219 if (hwndMemFrame)
1220 {
1221 WinSetWindowPos(hwndMemFrame,
1222 HWND_TOP,
1223 0, 0, 0, 0,
1224 SWP_ZORDER | SWP_SHOW | SWP_ACTIVATE);
1225 }
1226 }
1227}
1228
1229#else
1230void memdDummy(void)
1231{
1232 int i = 0;
1233}
1234#endif
1235
Note: See TracBrowser for help on using the repository browser.