home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / memmon.zip / MEMMON.PAS next >
Pascal/Delphi Source File  |  1997-05-10  |  10KB  |  437 lines

  1. {█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█}
  2. {█                                                   █}
  3. {█  Virtual Pascal Hack v1.10                        █}
  4. {█  Memory usage monitor routines for VP/2 v1.10     █}
  5. {█  ─────────────────────────────────────────────────█}
  6. {█  Copyright (C) 1997 fPrint UK Ltd                 █}
  7. {█  Written April-May 1997 by Allan Mertner          █}
  8. {█                                                   █}
  9. {█  NOTE: USE THIS UNIT AT YOUR OWN RISK.            █}
  10. {█                                                   █}
  11. {▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀}
  12.  
  13. unit MemMon;
  14.  
  15. interface
  16.  
  17. {$X+,Delphi+}
  18.  
  19. uses
  20.   Os2Base;
  21.  
  22. {---[ Reporting ]---}
  23.  
  24. procedure DisplayStandardData;
  25.   // Displays standard debugging information based on
  26.   // data collected in this unit
  27.  
  28. procedure DisplayUnFreedMemory;
  29.   // Displays a list of heap memory allocated but not
  30.   // freed, including allocation size and line number
  31.   // of allocating call
  32.  
  33.  
  34. {---[ Heap routines ]---}
  35.  
  36. function GetMaxMemoryUsed: Longint;
  37.   // Returns the maximum number of bytes allocated on the
  38.   // heap at any one time
  39.  
  40. function GetCurrentMemoryUsed: Longint;
  41.   // Returns the number of bytes current allocated on the
  42.   // heap
  43.  
  44.  
  45. {---[ Stack routines ]---}
  46.  
  47. function GetMaxStackUsed: Longint;
  48.   // Returns the maximum number of stack bytes allocated
  49.   // by the current thread
  50.  
  51. function GetMaxStackUsageLocation: String;
  52.   // Returns the file name and line number where the stack
  53.   // usage was at maximum for current thread
  54.  
  55. function GetMaxThreadStackUsed(TID: Longint): Longint;
  56.   // Returns the maximum number of stack bytes allocated
  57.   // by a specific thread
  58.  
  59. function GetMaxThreadStackUsageLocation(TID: Longint): String;
  60.   // Returns the file name and line number where the stack
  61.   // usage was at maximum for a specific thread
  62.  
  63. const
  64.   // Maximum number of threads supported for stack checking
  65.   MaxThreads = 200;
  66.  
  67. implementation
  68.  
  69. uses
  70.   VPUtils;
  71.  
  72. {---[ Heap routines ]---}
  73.  
  74. type
  75.   PAllocRec = ^TAllocRec;
  76.   TAllocRec = record
  77.     Size   : Longint;
  78.     Caller : Longint;
  79.     Addr   : Pointer;
  80.     Unused : Longint;
  81.   end;
  82.  
  83. const
  84.   InitialAllocs = 64;
  85.  
  86. var
  87.   Allocations : PAllocRec;
  88.   AllocCount  : Longint;
  89.   AllocIndex  : Longint;
  90.   AllocTotal  : Longint;
  91.   AllocMax    : Longint;
  92.  
  93. function GetCurrentMemoryUsed: Longint;
  94. begin
  95.   Result := AllocTotal;
  96. end;
  97.  
  98. function GetMaxMemoryUsed: Longint;
  99. begin
  100.   Result := AllocMax;
  101. end;
  102.  
  103.  
  104. {---[ Reporting ]---}
  105.  
  106. procedure DisplayStandardData;
  107. var
  108.   i: Longint;
  109. begin
  110.   { Output maximum stack usage for each thread: }
  111.   for i := 1 to MaxThreads do
  112.     if GetMaxThreadStackUsed(i) > 0 then
  113.       begin
  114.         Write('Stack usage, ');
  115.         if i = 1 then
  116.           writeln('main thread')
  117.         else
  118.           Writeln('thread #',i);
  119.         Writeln('  Stack used : ',GetMaxThreadStackUsed(i));
  120.         Writeln('  Location   : ',GetMaxThreadStackUsageLocation(i));
  121.       end;
  122.  
  123.   Writeln;
  124.   DisplayUnfreedMemory;
  125.  
  126.   Writeln;
  127.   Writeln('Current heap memory allocated: ',GetCurrentMemoryUsed,' bytes');
  128.   Writeln('Maximum heap memory allocated: ',GetMaxMemoryUsed,' bytes');
  129. end;
  130.  
  131. procedure DisplayUnFreedMemory;
  132. var
  133.   FileName : String;
  134.   LineNo   : Longint;
  135.   i        : Longint;
  136.   S        : String;
  137.   P        : PAllocRec;
  138. begin
  139.   if AllocIndex = 0 then
  140.     Writeln('All heap memory was freed')
  141.   else
  142.     begin
  143.       Writeln('The following memory blocks were not freed:');
  144.       Writeln('  Heap Address    Bytes   Allocation code');
  145.     end;
  146.   P := Allocations;
  147.   for i := 1 to AllocIndex do
  148.     begin
  149.       if GetLocationInfo(Ptr(P^.Caller-1), FileName, LineNo) <> nil then
  150.         begin
  151.           Str(LineNo, S);
  152.           S := FileName + ', Line ' + S;
  153.         end
  154.       else
  155.         S := '(return address is '+Int2Hex(P^.Caller, 8)+')';
  156.  
  157.       Writeln( '    @',Ptr2Hex(P^.Addr),'   ',P^.Size:7, '   '+S);
  158.       inc(P);
  159.     end;
  160. end;
  161.  
  162.  
  163. {---[ Stack routines ]---}
  164.  
  165. // Disable stack checking
  166. {$S-}
  167.  
  168. var
  169.   MinStack: Array[1..MaxThreads] of Cardinal;
  170.   MaxStack: Array[1..MaxThreads] of Cardinal;
  171.   Caller  : Array[1..MaxThreads] of Pointer;
  172.  
  173. function GetMaxThreadStackUsed(TID: Longint): Longint;
  174. begin
  175.   If not (TID in [1..MaxThreads]) or (MaxStack[TID] = 0) then
  176.     Result := 0
  177.   else
  178.     Result := MaxStack[TID]-MinStack[TID];
  179. end;
  180.  
  181. function GetMaxThreadStackUsageLocation(TID: Longint): String;
  182. var
  183.   FileName: String;
  184.   LineNo: Longint;
  185. begin
  186.   Result := '';
  187.   if TID in [1..MaxThreads] then
  188.     if GetLocationInfo(Caller[TID], FileName, LineNo) <> nil then
  189.       begin
  190.         Str(LineNo, Result);
  191.         Result := FileName + ', Line ' + Result;
  192.       end;
  193. end;
  194.  
  195. function GetMaxStackUsed: Longint;
  196. begin
  197.   Result := GetMaxThreadStackUsed(GetThreadID);
  198. end;
  199.  
  200. function GetMaxStackUsageLocation: String;
  201. begin
  202.   Result := GetMaxThreadStackUsageLocation(GetThreadID);
  203. end;
  204.  
  205. procedure Failed;
  206. begin
  207.   Writeln('Stack/Heap Usage Checking failed to install');
  208.   Writeln;
  209.   Writeln('This unit only works with Virtual Pascal for OS/2 v1.10,');
  210.   Writeln('and must be compiled with the compiler options found in');
  211.   Writeln('the original source files in order to work.');
  212.   Writeln;
  213.   Writeln('Program terminated.');
  214.   Halt(1);
  215. end;
  216.  
  217. { -----  Below this line, the code must be left unchanged to work }
  218.  
  219. {$S-,W-,Optimize+,T-,X+}
  220. procedure MyStackCheck;
  221. assembler; {$Frame-} {$Uses None} {$Alters None}
  222. asm
  223.     push   eax
  224.     push   ecx
  225.  
  226.     call   GetThreadID
  227.     dec    eax
  228.     mov    ecx,esp
  229.     cmp    ecx,[eax*4+offset MaxStack]
  230.     jle    @@NotBigger
  231.     mov    [eax*4+offset MaxStack],ecx
  232.   @@NotBigger:
  233.     cmp    ecx,[eax*4+offset MinStack]
  234.     jge    @@NotSmaller
  235.     mov    [eax*4+offset MinStack],ecx
  236.     mov    ecx,[esp+12]
  237.     mov    [eax*4+offset Caller],ecx
  238.   @@NotSmaller:
  239.  
  240.     pop    ecx
  241.     pop    eax
  242.  
  243.     xchg   [esp+8],eax
  244.     add    eax,1000h
  245. end;
  246.  
  247. var
  248.   MemGetOriginal : Longint;
  249.   MemFreeOriginal: Longint;
  250.   SaveEAX        : Longint;
  251.   SaveECX        : Longint;
  252.   SaveEDX        : Longint;
  253.   HeapSem        : Longint;
  254.  
  255. procedure IncreaseAllocation;
  256. var
  257.   P: Pointer;
  258. begin
  259.   GetMem(P, (AllocCount+InitialAllocs)*Sizeof(TAllocRec));
  260.   move(Allocations^, P^, AllocCount*Sizeof(TAllocRec));
  261.   FreeMem(Allocations, AllocCount*Sizeof(TAllocRec));
  262.   inc(AllocCount, InitialAllocs);
  263.   Allocations := P;
  264. end;
  265.  
  266. procedure MyMemGet;
  267. assembler; {$Frame-} {$Uses None} {$Alters None}
  268. asm
  269.     bt     HeapSem, 0
  270.     jnc    @@RetrySem
  271.  
  272.     call   MemGetOriginal
  273.     ret
  274.  
  275.   @@RetrySem:
  276.     bts    HeapSem, 1
  277.     jnc    @@Go
  278.     push   31
  279.     call   DosSleep
  280.     jmp    @@RetrySem
  281.  
  282.   @@Go:
  283.     mov    [SaveEAX],eax
  284.     mov    [SaveECX],ecx
  285.     mov    [SaveEDX],edx
  286.  
  287.     mov    ecx,AllocIndex
  288.     cmp    ecx,AllocCount
  289.     jl     @@EnoughMemory
  290.  
  291.     bts    HeapSem,0
  292.  
  293.     call   IncreaseAllocation
  294.     mov    ecx,AllocIndex
  295.  
  296.     btr    HeapSem,0
  297.  
  298.   @@EnoughMemory:
  299.     mov    edx,[Allocations]
  300.     shl    ecx,4
  301.     mov    [edx+ecx],eax
  302.     add    AllocTotal,eax
  303.     mov    eax,AllocTotal
  304.     cmp    eax,AllocMax
  305.     jle    @@NotBiggest
  306.     mov    AllocMax,eax
  307.  
  308.   @@NotBiggest:
  309.     mov    eax,[esp+4]
  310.     mov    [edx+ecx+4],eax
  311.  
  312.     mov    eax,[SaveEAX]
  313.  
  314.     call   MemGetOriginal
  315.  
  316.     mov    [edx+ecx+8],eax
  317.     inc    AllocIndex
  318.  
  319.     mov    edx,[SaveEDX]
  320.     mov    ecx,[SaveECX]
  321.  
  322.     and    HeapSem,$FD
  323. end;
  324.  
  325. procedure MyMemFree;
  326. assembler; {$Frame-} {$Uses None} {$Alters None}
  327. asm
  328.   @@RetrySem:
  329.     bts    HeapSem, 1
  330.     jnc    @@Go
  331.     push   31
  332.     call   DosSleep
  333.     jmp    @@RetrySem
  334.  
  335.   @@Go:
  336.     push   eax
  337.     push   esi
  338.     push   ecx
  339.     push   edx
  340.  
  341.     mov    edx,[Allocations]
  342.     mov    ecx,AllocIndex
  343.     dec    ecx
  344.     mov    esi,ecx
  345.     shl    esi,4
  346.  
  347.   @@Next:
  348.     cmp    ebx,[edx+esi+8]
  349.     je     @@found
  350.     sub    esi,16
  351.     loopne @@Next
  352.     jmp    @@NotFound
  353.  
  354.   @@Found:
  355.     sub    AllocTotal,eax
  356.     push   edi
  357.     add    esi,[Allocations]
  358.     mov    edi,esi
  359.     add    esi,16
  360.     mov    eax,ecx
  361.     mov    ecx,AllocCount
  362.     sub    ecx,eax
  363.     rep    movsd
  364.  
  365.     pop    edi
  366.     dec    AllocIndex
  367.  
  368.   @@NotFound:
  369.  
  370.     pop    edx
  371.     pop    ecx
  372.     pop    esi
  373.     pop    eax
  374.  
  375.     btr    HeapSem,1
  376.  
  377.     call   MemFreeOriginal
  378. end;
  379.  
  380. {$S+}
  381. procedure HackStackCheck;
  382. var
  383.   i: Longint;
  384.   StackCheck: Longint;
  385. begin
  386.   fillchar(MaxStack, Sizeof(MaxStack), 0);
  387.   for i := Low(MinStack) to High(MinStack) do
  388.     MinStack[i] := $7FFFFFFF;
  389.  
  390.   StackCheck := Ofs(HackStackCheck)+3;
  391.   inc(StackCheck, MemL[StackCheck]+4);
  392.  
  393.   if DosSetMem(Ptr(StackCheck), 10, pag_Write) <> 0 then Failed;
  394.   Mem [StackCheck]   := $E8;
  395.   MemL[StackCheck+1] := Longint(@MyStackCheck)-StackCheck-5;
  396.   MemL[StackCheck+5] := $90909090;
  397.   DosSetMem(Ptr(StackCheck), 10, pag_Default);
  398. end;
  399.  
  400. {$S-}
  401. procedure HackMemoryManager;
  402. var
  403.   p       : Pointer;
  404.   MemGet  : Longint;
  405.   MemFree : Longint;
  406. begin
  407.   GetMem(Allocations, InitialAllocs*Sizeof(TAllocRec));
  408.   FreeMem(P, 0);
  409.   AllocCount := InitialAllocs;
  410.   AllocIndex := 0;
  411.  
  412.   MemGet := Ofs(HackMemoryManager)+10;
  413.   inc(MemGet, MemL[MemGet]+4+4);
  414.   MemGetOriginal := MemL[MemGet+1]+MemGet+5;
  415.  
  416.   if DosSetMem(Ptr(MemGet), 5, pag_Write) <> 0 then Failed;
  417.   MemL[MemGet+1] := Longint(@MyMemGet)-MemGet-5;
  418.   DosSetMem(Ptr(MemGet), 5, pag_Default);
  419.  
  420.   MemFree := Ofs(HackMemoryManager)+23;
  421.   inc(MemFree, MemL[MemFree]+4);
  422.   MemFreeOriginal := MemL[MemFree+11]+MemFree+15;
  423.  
  424.   if DosSetMem(Ptr(MemFree), 5, pag_Write) <> 0 then Failed;
  425.   MemL[MemFree+11] := Longint(@MyMemFree)-MemFree-15;
  426.   DosSetMem(Ptr(MemFree), 5, pag_Default);
  427.  
  428.   AllocMax   := 0;
  429.   AllocTotal := 0;
  430. end;
  431.  
  432. begin
  433.   HackStackCheck;
  434.   HackMemoryManager;
  435. end.
  436.  
  437.