home *** CD-ROM | disk | FTP | other *** search
- /*
- COUNTMEM.C -- Totals memory consumption (bytes and selectors)
- by walking the global heap
-
- From Chapter 5 of "Undocumented Windows" (Addison-Wesley 1992)
- by Andrew Schulman, Dave Maxey and Matt Pietrek
-
- Build using: WINIOBC COUNTMEM HANDLES (for Borland C++ v3.00)
- WINIOMS COUNTMEM HANDLES (for Microsoft C/SDK)
-
- The point of this code is to illustrate the arrangement of the
- Windows global heap info structure and arenas. If you actually
- want to walk the heap in commercial code, use TOOLHELP, ok? Please?
-
- In TOOLHELP, it's as simple as:
- GLOBALENTRY ge;
- BOOL ok;
- ge.dwSize = sizeof(ge);
- ok = GlobalFirst(&ge, GLOBAL_ALL);
- while (ok)
- {
- add(ge.hBlock, ge.hOwner, ge.dwBlockSize);
- ok = GlobalNext(&ge, GLOBAL_ALL);
- }
-
- On the other hand, with TOOLHELP it is too easy to forget to add
- in the size of the arenas themselves, and in fact TOOLHELP doesn't
- tell you the size of the arenas. For an example of where TOOLHELP
- falls short, see the code in add() below with the comment "add
- this Global Arena to the totals for this owner." It would be nice
- if TOOLHELP took this sort of thing into account.
-
- Andrew Schulman, March 1992; revised May 1992
- */
-
- #include <windows.h>
- #include <stdlib.h>
- #include <string.h>
- #include <assert.h>
- #include <dos.h>
- #include "winio.h"
- #include "handles.h"
-
- static DWORD (FAR PASCAL *GlobalMasterHandle)(void);
- static DWORD (FAR PASCAL *GetSelectorLimit)(WORD wSel);
-
- #define GETPROC(modname, funcname) \
- GetProcAddress(GetModuleHandle(modname), (funcname))
-
- #pragma pack(1)
-
- /* The only difference between HEAPINFO and HEAPINFO32
- is the size of fields such as "first" and "last". The
- following is borrowed from the Windows 3.1 DDK include
- file WINKERN.INC. However, not all of these fields
- appear to actually be used! */
- #define DEFINE_HEAPINFO_STRUCT(STRUCTNAME, WORDDWORD) \
- typedef struct { \
- WORD check; \
- WORD freeze; \
- WORD count; \
- WORDDWORD first; \
- WORDDWORD last; \
- BYTE ncompact; \
- BYTE dislevel; \
- WORDDWORD distotal; \
- WORD htable; \
- WORD hfree; \
- WORD hdelta; \
- WORD hexpand; \
- WORD pstats; \
- WORD lrulock; \
- WORDDWORD lruchain; \
- WORD lrucount; \
- WORDDWORD reserve; \
- WORDDWORD disfence; \
- WORD free_count; \
- WORD alt_first; \
- WORD alt_last; \
- WORD alt_count; \
- WORD alt_lruchain; \
- WORD alt_lrucount; \
- WORD alt_reserve; \
- WORD alt_disfence; \
- WORD alt_pPhantom; \
- WORD disfence_hi; \
- WORD flags; \
- BYTE stats[56/4]; \
- } STRUCTNAME;
-
- DEFINE_HEAPINFO_STRUCT(GLOBAL_HEAP, WORD);
- DEFINE_HEAPINFO_STRUCT(GLOBAL_HEAP_32, DWORD);
-
- /* The Global Arena structures are totally different
- for 16-bit and 32-bit KERNEL. */
- typedef struct {
- BYTE count;
- WORD owner;
- WORD paragraphs;
- BYTE flags;
- WORD prev;
- WORD next;
- WORD handle;
- WORD lruprev;
- WORD lrunext;
- } GLOBAL_ARENA;
-
- typedef struct {
- DWORD next;
- DWORD prev;
- DWORD base;
- DWORD bytes;
- WORD handle;
- WORD owner;
- BYTE count;
- BYTE pglock;
- BYTE flags;
- BYTE selcount;
- DWORD lruprev;
- DWORD lrunext;
- } GLOBAL_ARENA_32;
-
- /* special owner types; otherwise owner is module, task, or PSP */
- #define SENTINEL ((WORD) -1)
- #define BURGERMASTER ((WORD) -3)
- #define NOT_THERE ((WORD) -4)
- #define PHANTOM ((WORD) -5)
- #define WRAITH ((WORD) -6)
- #define BOGUS ((WORD) -7)
- #define FREE (0)
-
- void walk_heap(WORD wGlobalHeap);
- char *owner_name(WORD wOwner);
- void add(HANDLE h, WORD wOwner, DWORD dwBytes);
- void print_totals(void);
-
- static HWND main_hwnd;
- static WORD wGlobalHeap; /* Burgermaster */
- static int k1632;
- static int arena_size;
-
- main()
- {
- winio_about("COUNTMEM"
- "\nTotals memory consumption (bytes and selectors)"
- "\nby walking the global heap"
- "\n\nFrom Chapter 5 of"
- "\n\"Undocumented Windows\" (Addison-Wesley, 1992)"
- "\nby Andrew Schulman, David Maxey and Matt Pietrek"
- );
-
- main_hwnd = winio_current();
-
- k1632 = Kernel1632();
- if (k1632 == 16)
- arena_size = sizeof(GLOBAL_ARENA);
- else
- arena_size = sizeof(GLOBAL_ARENA_32);
-
- GlobalMasterHandle = GETPROC("KERNEL", "GLOBALMASTERHANDLE");
- GetSelectorLimit = GETPROC("KERNEL", "GETSELECTORLIMIT");
-
- // use HIWORD: take selector, not handle
- if ((wGlobalHeap = HIWORD(GlobalMasterHandle())) != NULL)
- walk_heap(wGlobalHeap);
- else
- fail("Cannot find Windows global heap");
-
- return 0;
- }
-
- void walk_heap(WORD wGlobalHeap)
- {
- int i;
-
- winio_clear(main_hwnd);
-
- printf("Burgermaster @ %04Xh\n", wGlobalHeap);
-
- /* in 3.1+, BURGERMASTER itself is not in the linked list */
- if (GetVersion() >= 0x0a03)
- add(wGlobalHeap, BURGERMASTER,
- GetSelectorLimit(wGlobalHeap)+1);
-
- /* Turn off WINIO display while walking the global heap.
- WINIO yields when it displays, and we don't want to yield
- here, because then other tasks might go and change the
- state of the global heap while we're in the middle of
- the walk. Actually, we're not printing here, but it's
- important to understand that YOU CANNOT YIELD IN THIS LOOP! */
- winio_setpaint(main_hwnd, FALSE);
-
- if (k1632 == 16) // 16-bit KERNEL
- {
- GLOBAL_ARENA far *lpga;
- GLOBAL_HEAP far *lphi = MK_FP(wGlobalHeap, 0);
- WORD wSel = lphi->first; // first entry
- WORD wCount = lphi->count; // number of entries
-
- for (i=0; i<wCount; i++)
- {
- /* In 16-bit KERNEL, lphi->first and lpga->next
- are selectors to arenas. */
- lpga = MK_FP(wSel, 0);
- add(lpga->handle, lpga->owner, (long) lpga->paragraphs << 4);
- wSel = lpga->next; // follow linked list
- }
- }
- else // 32-bit KERNEL
- {
- GLOBAL_ARENA_32 far *lpga32;
- GLOBAL_HEAP_32 far *lphi32 = MK_FP(wGlobalHeap, 0);
- DWORD dwOffset = lphi32->first; // first entry
- WORD wCount = lphi32->count; // number of entries
-
- for (i=0; i<wCount; i++)
- {
- /* In 32-bit KERNEL, lphi32->first and lpga32->next
- are not selectors, but offsets into the master table */
- lpga32 = MK_FP(wGlobalHeap, dwOffset);
- add(lpga32->handle, lpga32->owner, lpga32->bytes);
- dwOffset = lpga32->next; // follow linked list
- if (dwOffset > 0xFFFFUL)
- fail("Can't access all of heap from 16-bit code");
- }
- }
- winio_setpaint(main_hwnd, TRUE);
-
- print_totals();
- }
-
- /**********************************************************************/
-
- typedef struct {
- WORD owner;
- WORD items;
- DWORD total;
- DWORD discard_total;
- DWORD code_total;
- } TOTAL;
-
- #define MAX_TOTAL 1024
-
- static TOTAL totals[MAX_TOTAL] = {0} ;
- static int num_totals = 0;
- static DWORD low_total = 0; /* amount below 1 megabyte */
- static DWORD code_total = 0;
- static DWORD data_total = 0;
-
- /* Add a Global Arena to the running total */
- void add(HANDLE h, WORD wOwner, DWORD dwBytes)
- {
- extern DWORD FAR PASCAL GetSelectorBase(WORD wSel);
- TOTAL *t;
- WORD wFlags;
- int i;
-
- /* If owner is a PSP or a task handle,
- find the corresponding module handle */
- if (IsValidPSP(wOwner))
- {
- HANDLE hTask;
- if ((hTask = hTask_from_PSP(wOwner)) != NULL)
- wOwner = HMODULE_FROM_HTASK(hTask);
- }
- else if (IsValidTask(wOwner))
- wOwner = HMODULE_FROM_HTASK(wOwner);
-
- /* Find the owner in the table */
- for (i=0, t=totals; i<num_totals; i++, t++)
- if (t->owner == wOwner)
- break;
- if (i == num_totals) // not in table yet
- {
- if (num_totals >= MAX_TOTAL)
- fail("totals overflow!");
- t = &totals[num_totals];
- t->owner = wOwner;
- num_totals++;
- }
-
- /* add this Global Arena to the totals for this owner */
- t->total += dwBytes;
- t->total += arena_size; // count the arena as part of total
- if (k1632 == 16)
- t->items += 2; // 16-bit KERNEL requires two selectors/block
- else
- t->items++;
-
- /* We could of course pull the flags right out of the Global
- Arena structure. But taking the handles produced by
- walking the Global Heap, and passing them to GlobalFlags(),
- shows that walking the heap could be used simply as a way to
- enumerate handles, leaving everything else to documented
- functions. */
- if (verr(wOwner)) // ensure has genuine owner before do GlobalFlags
- if ((wFlags = GlobalFlags(h)) != 0)
- if (wFlags & GMEM_DISCARDABLE)
- t->discard_total += dwBytes;
-
- /* To find out if it's CODE or DATA, we use the Intel LAR (Load
- Access Rights) instruction. We could also use the undocumented
- Windows SelectorAccessRights() function. */
- if ((lar(h) & CODEDATA_MASK) == CODE)
- code_total += dwBytes;
- else
- data_total += dwBytes;
-
- /* To find its linear base address, we use the undocumented
- Windows GetSelectorBase() function. If the base address is
- less than one megabyte, it's low memory (equivalent to a
- GlobalDosAlloc()) */
- if (GetSelectorBase(h) < 0x100000L)
- low_total += dwBytes;
- }
-
- void print_totals(void)
- {
- TOTAL *t;
- DWORD total=0, discard=0, free_total=0;
- WORD items=0;
- int i;
-
- printf("# Sel\tBytes (Discardable)\n");
-
- for (i=0, t=totals; i<num_totals; i++, t++)
- {
- printf("%d\t%7lu %7lu\t%s\n",
- t->items, t->total, t->discard_total,
- owner_name(t->owner));
-
- /* get the grand total */
- if (t->owner == FREE)
- free_total = t->total;
- else
- total += t->total;
- discard += t->discard_total;
- items += t->items;
- }
-
- /* display the grand total */
- printf("\n");
- printf("Total: %8lu bytes (%d selectors)\n", total, items);
- printf("Discardable: %8lu bytes\n", discard);
- printf("Free: %8lu bytes\n", free_total);
- printf("Low memory: %8lu bytes\n", low_total);
- printf("Code: %8lu bytes\n", code_total);
- printf("Data: %8lu bytes\n", data_total);
- }
-
- char *owner_name(WORD wOwner)
- {
- static char buf[128];
-
- switch (wOwner) // assumed to be module handle or special
- {
- case SENTINEL: return "SENTINEL";
- case BURGERMASTER: return "BURGERMASTER";
- case NOT_THERE: return "NOT_THERE";
- case PHANTOM: return "PHANTOM";
- case WRAITH: return "WRAITH";
- case BOGUS: return "BOGUS";
- case FREE: return "FREE";
- default:
- if (IsValidModuleHandle(wOwner))
- {
- /* To get short name, could use
- GetModuleNameFromHandle() in WINMOD.C */
- GetModuleFileName(wOwner, buf, 128);
- return buf;
- }
- else
- return "??";
- }
- }
-
-