home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 10 / 10.iso / l / l430 / 1.ddi / CHAP5.ZIP / COUNTMEM.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-09  |  10.3 KB  |  377 lines

  1. /*
  2.     COUNTMEM.C -- Totals memory consumption (bytes and selectors)
  3.     by walking the global heap
  4.  
  5.     From Chapter 5 of "Undocumented Windows" (Addison-Wesley 1992)
  6.     by Andrew Schulman, Dave Maxey and Matt Pietrek
  7.  
  8.     Build using: WINIOBC COUNTMEM HANDLES (for Borland C++ v3.00)
  9.                  WINIOMS COUNTMEM HANDLES (for Microsoft C/SDK)
  10.  
  11.    The point of this code is to illustrate the arrangement of the
  12.    Windows global heap info structure and arenas.  If you actually
  13.    want to walk the heap in commercial code, use TOOLHELP, ok? Please?
  14.        
  15.    In TOOLHELP, it's as simple as:
  16.        GLOBALENTRY ge;
  17.        BOOL ok;
  18.        ge.dwSize = sizeof(ge);
  19.        ok = GlobalFirst(&ge, GLOBAL_ALL);
  20.        while (ok)
  21.        {
  22.            add(ge.hBlock, ge.hOwner, ge.dwBlockSize);
  23.            ok = GlobalNext(&ge, GLOBAL_ALL);
  24.        }
  25.        
  26.     On the other hand, with TOOLHELP it is too easy to forget to add
  27.     in the size of the arenas themselves, and in fact TOOLHELP doesn't
  28.     tell you the size of the arenas. For an example of where TOOLHELP
  29.     falls short, see the code in add() below with the comment "add
  30.     this Global Arena to the totals for this owner." It would be nice
  31.     if TOOLHELP took this sort of thing into account.
  32.  
  33.     Andrew Schulman, March 1992; revised May 1992
  34. */        
  35.  
  36. #include <windows.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #include <assert.h>
  40. #include <dos.h>
  41. #include "winio.h"
  42. #include "handles.h"
  43.  
  44. static DWORD (FAR PASCAL *GlobalMasterHandle)(void);
  45. static DWORD (FAR PASCAL *GetSelectorLimit)(WORD wSel);
  46.  
  47. #define GETPROC(modname, funcname) \
  48.     GetProcAddress(GetModuleHandle(modname), (funcname))
  49.  
  50. #pragma pack(1)
  51.  
  52. /*    The only difference between HEAPINFO and HEAPINFO32
  53.     is the size of fields such as "first" and "last".  The
  54.     following is borrowed from the Windows 3.1 DDK include
  55.     file WINKERN.INC.  However, not all of these fields
  56.     appear to actually be used! */
  57. #define DEFINE_HEAPINFO_STRUCT(STRUCTNAME, WORDDWORD) \
  58. typedef struct { \
  59.     WORD check; \
  60.     WORD freeze; \
  61.     WORD count; \
  62.     WORDDWORD first; \
  63.     WORDDWORD last; \
  64.     BYTE ncompact; \
  65.     BYTE dislevel; \
  66.     WORDDWORD distotal; \
  67.     WORD htable; \
  68.     WORD hfree; \
  69.     WORD hdelta; \
  70.     WORD hexpand; \
  71.     WORD pstats; \
  72.     WORD lrulock; \
  73.     WORDDWORD lruchain; \
  74.     WORD lrucount; \
  75.     WORDDWORD reserve; \
  76.     WORDDWORD disfence; \
  77.     WORD free_count; \
  78.     WORD alt_first; \
  79.     WORD alt_last; \
  80.     WORD alt_count; \
  81.     WORD alt_lruchain; \
  82.     WORD alt_lrucount; \
  83.     WORD alt_reserve; \
  84.     WORD alt_disfence; \
  85.     WORD alt_pPhantom; \
  86.     WORD disfence_hi; \
  87.     WORD flags; \
  88.     BYTE stats[56/4]; \
  89.     } STRUCTNAME;
  90.  
  91. DEFINE_HEAPINFO_STRUCT(GLOBAL_HEAP, WORD);
  92. DEFINE_HEAPINFO_STRUCT(GLOBAL_HEAP_32, DWORD);
  93.  
  94. /*    The Global Arena structures are totally different
  95.     for 16-bit and 32-bit KERNEL. */
  96. typedef struct {
  97.     BYTE count;
  98.     WORD owner;
  99.     WORD paragraphs;
  100.     BYTE flags;
  101.     WORD prev;
  102.     WORD next;
  103.     WORD handle;
  104.     WORD lruprev;
  105.     WORD lrunext;
  106.     } GLOBAL_ARENA;
  107.     
  108. typedef struct {
  109.     DWORD next;
  110.     DWORD prev;
  111.     DWORD base;
  112.     DWORD bytes;
  113.     WORD handle;
  114.     WORD owner;
  115.     BYTE count;
  116.     BYTE pglock;
  117.     BYTE flags;
  118.     BYTE selcount;
  119.     DWORD lruprev;
  120.     DWORD lrunext;
  121.     } GLOBAL_ARENA_32;
  122.  
  123. /* special owner types; otherwise owner is module, task, or PSP */
  124. #define SENTINEL        ((WORD) -1)
  125. #define BURGERMASTER    ((WORD) -3)
  126. #define NOT_THERE       ((WORD) -4)
  127. #define PHANTOM         ((WORD) -5)
  128. #define WRAITH          ((WORD) -6)
  129. #define BOGUS           ((WORD) -7)
  130. #define FREE            (0)
  131.  
  132. void walk_heap(WORD wGlobalHeap);
  133. char *owner_name(WORD wOwner);
  134. void add(HANDLE h, WORD wOwner, DWORD dwBytes);
  135. void print_totals(void);
  136.  
  137. static HWND main_hwnd;
  138. static WORD wGlobalHeap;    /* Burgermaster */
  139. static int k1632;
  140. static int arena_size;
  141.  
  142. main()
  143. {
  144.     winio_about("COUNTMEM"
  145.         "\nTotals memory consumption (bytes and selectors)"
  146.         "\nby walking the global heap"
  147.         "\n\nFrom Chapter 5 of"
  148.         "\n\"Undocumented Windows\" (Addison-Wesley, 1992)"
  149.         "\nby Andrew Schulman, David Maxey and Matt Pietrek"
  150.         );
  151.     
  152.     main_hwnd = winio_current();
  153.     
  154.     k1632 = Kernel1632();
  155.     if (k1632 == 16)
  156.         arena_size = sizeof(GLOBAL_ARENA);
  157.     else
  158.         arena_size = sizeof(GLOBAL_ARENA_32);
  159.  
  160.     GlobalMasterHandle = GETPROC("KERNEL", "GLOBALMASTERHANDLE");
  161.     GetSelectorLimit = GETPROC("KERNEL", "GETSELECTORLIMIT");
  162.     
  163.     // use HIWORD:  take selector, not handle
  164.     if ((wGlobalHeap = HIWORD(GlobalMasterHandle())) != NULL)
  165.         walk_heap(wGlobalHeap);
  166.     else
  167.         fail("Cannot find Windows global heap");
  168.     
  169.     return 0;
  170. }
  171.  
  172. void walk_heap(WORD wGlobalHeap)
  173. {
  174.     int i;
  175.     
  176.     winio_clear(main_hwnd);
  177.     
  178.     printf("Burgermaster @ %04Xh\n", wGlobalHeap);
  179.     
  180.     /* in 3.1+, BURGERMASTER itself is not in the linked list */
  181.     if (GetVersion() >= 0x0a03)
  182.         add(wGlobalHeap, BURGERMASTER, 
  183.             GetSelectorLimit(wGlobalHeap)+1);
  184.  
  185.     /*    Turn off WINIO display while walking the global heap.
  186.         WINIO yields when it displays, and we don't want to yield
  187.         here, because then other tasks might go and change the
  188.         state of the global heap while we're in the middle of
  189.         the walk.  Actually, we're not printing here, but it's
  190.         important to understand that YOU CANNOT YIELD IN THIS LOOP! */
  191.     winio_setpaint(main_hwnd, FALSE);
  192.         
  193.     if (k1632 == 16)    // 16-bit KERNEL
  194.     {
  195.         GLOBAL_ARENA far *lpga;
  196.         GLOBAL_HEAP far *lphi = MK_FP(wGlobalHeap, 0);
  197.         WORD wSel = lphi->first;    // first entry            
  198.         WORD wCount = lphi->count;    // number of entries
  199.             
  200.         for (i=0; i<wCount; i++)
  201.         {
  202.             /*    In 16-bit KERNEL, lphi->first and lpga->next
  203.                 are selectors to arenas. */
  204.             lpga = MK_FP(wSel, 0);
  205.             add(lpga->handle, lpga->owner, (long) lpga->paragraphs << 4);
  206.             wSel = lpga->next;    // follow linked list                
  207.         }
  208.     }
  209.     else    // 32-bit KERNEL
  210.     {
  211.         GLOBAL_ARENA_32 far *lpga32;
  212.         GLOBAL_HEAP_32 far *lphi32 = MK_FP(wGlobalHeap, 0);
  213.         DWORD dwOffset = lphi32->first;    // first entry
  214.         WORD wCount = lphi32->count;    // number of entries
  215.             
  216.         for (i=0; i<wCount; i++)
  217.         {
  218.             /*    In 32-bit KERNEL, lphi32->first and lpga32->next
  219.                 are not selectors, but offsets into the master table */
  220.             lpga32 = MK_FP(wGlobalHeap, dwOffset);
  221.             add(lpga32->handle, lpga32->owner, lpga32->bytes);
  222.             dwOffset = lpga32->next;    // follow linked list
  223.             if (dwOffset > 0xFFFFUL)
  224.                 fail("Can't access all of heap from 16-bit code");
  225.         }
  226.     }
  227.     winio_setpaint(main_hwnd, TRUE);
  228.     
  229.     print_totals();
  230. }
  231.  
  232. /**********************************************************************/
  233.  
  234. typedef struct {
  235.     WORD owner; 
  236.     WORD items;
  237.     DWORD total; 
  238.     DWORD discard_total;
  239.     DWORD code_total;
  240.     } TOTAL;
  241.     
  242. #define MAX_TOTAL   1024
  243.     
  244. static TOTAL totals[MAX_TOTAL] = {0} ;
  245. static int num_totals = 0;
  246. static DWORD low_total = 0;        /* amount below 1 megabyte */
  247. static DWORD code_total = 0;
  248. static DWORD data_total = 0;
  249.  
  250. /* Add a Global Arena to the running total */
  251. void add(HANDLE h, WORD wOwner, DWORD dwBytes)
  252. {
  253.     extern DWORD FAR PASCAL GetSelectorBase(WORD wSel);
  254.     TOTAL *t;
  255.     WORD wFlags;
  256.     int i;
  257.  
  258.     /* If owner is a PSP or a task handle, 
  259.        find the corresponding module handle */
  260.     if (IsValidPSP(wOwner))
  261.     {
  262.         HANDLE hTask;
  263.         if ((hTask = hTask_from_PSP(wOwner)) != NULL)
  264.             wOwner = HMODULE_FROM_HTASK(hTask);
  265.     }
  266.     else if (IsValidTask(wOwner))
  267.         wOwner = HMODULE_FROM_HTASK(wOwner);
  268.     
  269.     /* Find the owner in the table */
  270.     for (i=0, t=totals; i<num_totals; i++, t++)
  271.         if (t->owner == wOwner)
  272.             break;
  273.     if (i == num_totals)    // not in table yet
  274.     {
  275.         if (num_totals >= MAX_TOTAL)
  276.             fail("totals overflow!");
  277.         t = &totals[num_totals];
  278.         t->owner = wOwner;
  279.         num_totals++;
  280.     }
  281.  
  282.     /* add this Global Arena to the totals for this owner */
  283.     t->total += dwBytes;
  284.     t->total += arena_size;    // count the arena as part of total
  285.     if (k1632 == 16) 
  286.         t->items += 2;        // 16-bit KERNEL requires two selectors/block
  287.     else
  288.         t->items++;
  289.     
  290.     /*    We could of course pull the flags right out of the Global
  291.         Arena structure.  But taking the handles produced by
  292.         walking the Global Heap, and passing them to GlobalFlags(),
  293.         shows that walking the heap could be used simply as a way to
  294.         enumerate handles, leaving everything else to documented
  295.         functions. */
  296.     if (verr(wOwner))    // ensure has genuine owner before do GlobalFlags
  297.         if ((wFlags = GlobalFlags(h)) != 0)
  298.             if (wFlags & GMEM_DISCARDABLE)
  299.                 t->discard_total += dwBytes;
  300.             
  301.     /*    To find out if it's CODE or DATA, we use the Intel LAR (Load
  302.         Access Rights) instruction.  We could also use the undocumented
  303.         Windows SelectorAccessRights() function. */
  304.     if ((lar(h) & CODEDATA_MASK) == CODE)
  305.         code_total += dwBytes;
  306.     else
  307.         data_total += dwBytes;
  308.             
  309.     /*    To find its linear base address, we use the undocumented
  310.         Windows GetSelectorBase() function. If the base address is
  311.         less than one megabyte, it's low memory (equivalent to a
  312.         GlobalDosAlloc()) */
  313.     if (GetSelectorBase(h) < 0x100000L)
  314.         low_total += dwBytes;
  315. }
  316.  
  317. void print_totals(void)
  318. {
  319.     TOTAL *t;
  320.     DWORD total=0, discard=0, free_total=0;
  321.     WORD items=0;
  322.     int i;
  323.     
  324.     printf("# Sel\tBytes (Discardable)\n");
  325.     
  326.     for (i=0, t=totals; i<num_totals; i++, t++)
  327.     {
  328.         printf("%d\t%7lu   %7lu\t%s\n", 
  329.             t->items, t->total, t->discard_total,
  330.             owner_name(t->owner));
  331.         
  332.         /* get the grand total */
  333.         if (t->owner == FREE)
  334.             free_total = t->total;
  335.         else
  336.             total += t->total;
  337.         discard += t->discard_total;
  338.         items += t->items;
  339.     }
  340.     
  341.     /* display the grand total */
  342.     printf("\n");
  343.     printf("Total:       %8lu bytes (%d selectors)\n", total, items);
  344.     printf("Discardable: %8lu bytes\n", discard);
  345.     printf("Free:        %8lu bytes\n", free_total);
  346.     printf("Low memory:  %8lu bytes\n", low_total);
  347.     printf("Code:        %8lu bytes\n", code_total);
  348.     printf("Data:        %8lu bytes\n", data_total);
  349. }
  350.  
  351. char *owner_name(WORD wOwner)
  352. {
  353.     static char buf[128];
  354.     
  355.     switch (wOwner)    // assumed to be module handle or special
  356.     {
  357.         case SENTINEL:        return "SENTINEL";
  358.         case BURGERMASTER:    return "BURGERMASTER";
  359.         case NOT_THERE:        return "NOT_THERE";
  360.         case PHANTOM:        return "PHANTOM";
  361.         case WRAITH:        return "WRAITH";
  362.         case BOGUS:            return "BOGUS";
  363.         case FREE:            return "FREE";
  364.         default:            
  365.             if (IsValidModuleHandle(wOwner))
  366.             {   
  367.                 /* To get short name, could use
  368.                    GetModuleNameFromHandle() in WINMOD.C */
  369.                 GetModuleFileName(wOwner, buf, 128);
  370.                 return buf;
  371.             }
  372.             else
  373.                 return "??";
  374.     }
  375. }
  376.  
  377.