FastMMUsageTracker.pas 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187
  1. (*
  2. Fast Memory Manager Usage Tracker 2.00
  3. Description:
  4. - Shows FastMM4 allocation usage
  5. - Shows VM Memory in graphical map
  6. - Free
  7. - Commit
  8. - Reserved
  9. - EXE (Red)
  10. - DLLs (Blue)
  11. - VM Dump of the whole process
  12. (2GB standard, 3GB with the /3G switch set, and 4GB under WoW64)
  13. - General Information
  14. - System memory usage
  15. - Process memory usage
  16. - 5 Largest contiguous free VM memory spaces
  17. - FastMM4 summary information
  18. Usage:
  19. - Add the FastMMUsageTracker unit
  20. - Add the ShowFastMMUsageTracker procedure to an event
  21. - FastMMUsageTracker form should not be autocreated
  22. Notes:
  23. - Consider setting the base adress of your BPLs & DLLs or use Microsoft's
  24. ReBase.exe to set third party BPLs and DLLs. Libraries that do not have to
  25. be relocated can be shared across processes, thus conserving system
  26. resources.
  27. - The first of the "Largest contiguous free VM memory spaces" gives you an
  28. indication of the largest single memory block that can be allocated.
  29. Change log:
  30. Version 2.10 (22 September 2009):
  31. - New usage tracker implemented by Hanspeter Widmer with many new features.
  32. (Thanks Hanspeter!);
  33. - Colour coding of changes in the allocation map added by Murray McGowan
  34. (red for an increase in usage, green for a decrease). (Thanks Murray!)
  35. *)
  36. unit FastMMUsageTracker;
  37. interface
  38. uses
  39. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  40. Dialogs, StdCtrls, ExtCtrls, Grids, Buttons, ComCtrls, Menus, FastMM4;
  41. type
  42. TChunkStatusEx = (
  43. {Items that correspond to the same entry in TChunkStatus}
  44. csExUnallocated,
  45. csExAllocated,
  46. csExReserved,
  47. csExSysAllocated,
  48. csExSysReserved,
  49. {TChunkStatusEx additional detail}
  50. csExSysExe,
  51. csExSysDLL);
  52. TMemoryMapEx = array[0..65535] of TChunkStatusEx;
  53. TfFastMMUsageTracker = class(TForm)
  54. tTimer: TTimer;
  55. bClose: TBitBtn;
  56. bUpdate: TBitBtn;
  57. ChkAutoUpdate: TCheckBox;
  58. smVMDump: TPopupMenu;
  59. smMM4Allocation: TPopupMenu;
  60. smGeneralInformation: TPopupMenu;
  61. miVMDumpCopyAlltoClipboard: TMenuItem;
  62. miGeneralInformationCopyAlltoClipboard: TMenuItem;
  63. siMM4AllocationCopyAlltoClipboard: TMenuItem;
  64. pcUsageTracker: TPageControl;
  65. tsAllocation: TTabSheet;
  66. tsVMGraph: TTabSheet;
  67. tsVMDump: TTabSheet;
  68. tsGeneralInformation: TTabSheet;
  69. mVMStatistics: TMemo;
  70. sgVMDump: TStringGrid;
  71. Label1: TLabel;
  72. Label2: TLabel;
  73. Label3: TLabel;
  74. eAddress: TEdit;
  75. eState: TEdit;
  76. eDLLName: TEdit;
  77. ChkSmallGraph: TCheckBox;
  78. sgBlockStatistics: TStringGrid;
  79. dgMemoryMap: TDrawGrid;
  80. procedure FormClose(Sender: TObject; var Action: TCloseAction);
  81. procedure tTimerTimer(Sender: TObject);
  82. procedure FormCreate(Sender: TObject);
  83. procedure bCloseClick(Sender: TObject);
  84. procedure dgMemoryMapDrawCell(Sender: TObject; ACol, ARow: Integer;
  85. Rect: TRect; State: TGridDrawState);
  86. procedure dgMemoryMapSelectCell(Sender: TObject; ACol, ARow: Integer;
  87. var CanSelect: Boolean);
  88. procedure bUpdateClick(Sender: TObject);
  89. procedure ChkAutoUpdateClick(Sender: TObject);
  90. procedure ChkSmallGraphClick(Sender: TObject);
  91. procedure sgVMDumpMouseDown(Sender: TObject; Button: TMouseButton;
  92. Shift: TShiftState; X, Y: Integer);
  93. procedure sgVMDumpMouseUp(Sender: TObject; Button: TMouseButton;
  94. Shift: TShiftState; X, Y: Integer);
  95. procedure sgVMDumpDrawCell(Sender: TObject; ACol, ARow: Integer;
  96. Rect: TRect; State: TGridDrawState);
  97. procedure miVMDumpCopyAlltoClipboardClick(Sender: TObject);
  98. procedure miGeneralInformationCopyAlltoClipboardClick(Sender: TObject);
  99. procedure siMM4AllocationCopyAlltoClipboardClick(Sender: TObject);
  100. procedure sgBlockStatisticsDrawCell(Sender: TObject; ACol,
  101. ARow: Integer; Rect: TRect; State: TGridDrawState);
  102. private
  103. {The current and previous memory manager states}
  104. FMemoryManagerState, FPrevMemoryManagerState: TMemoryManagerState;
  105. FMemoryMapEx: TMemoryMapEx;
  106. AddressSpacePageCount: Integer;
  107. OR_VMDumpDownCell: TGridCoord;
  108. procedure HeaderClicked(AGrid: TStringgrid; const ACell: TGridCoord);
  109. procedure SortGrid(grid: TStringgrid; PB_Nummeric: Boolean; byColumn: Integer; ascending: Boolean);
  110. procedure UpdateGraphMetrics;
  111. public
  112. {Refreshes the display}
  113. procedure RefreshSnapShot;
  114. end;
  115. function ShowFastMMUsageTracker: TfFastMMUsageTracker;
  116. implementation
  117. uses
  118. Clipbrd, PsAPI;
  119. {$R *.dfm}
  120. const
  121. SystemBasicInformation = 0;
  122. SystemPerformanceInformation = 2;
  123. SystemTimeInformation = 3;
  124. type
  125. {To get access to protected methods}
  126. TLocalStringGrid = class(TStringGrid);
  127. TMemoryStatusEx = packed record
  128. dwLength: DWORD;
  129. dwMemoryLoad: DWORD;
  130. ullTotalPhys: Int64;
  131. ullAvailPhys: Int64;
  132. ullTotalPageFile: Int64;
  133. ullAvailPageFile: Int64;
  134. ullTotalVirtual: Int64;
  135. ullAvailVirtual: Int64;
  136. ullAvailExtendedVirtual: Int64;
  137. end;
  138. PMemoryStatusEx = ^TMemoryStatusEx;
  139. LPMEMORYSTATUSEX = PMemoryStatusEx;
  140. TP_GlobalMemoryStatusEx = function(
  141. var PR_MemStatusEx: TMemoryStatusEx): LongBool; stdcall;
  142. TSystem_Basic_Information = packed record
  143. dwUnknown1: DWORD;
  144. uKeMaximumIncrement: ULONG;
  145. uPageSize: ULONG;
  146. uMmNumberOfPhysicalPages: ULONG;
  147. uMmLowestPhysicalPage: ULONG;
  148. uMmHighestPhysicalPage: ULONG;
  149. uAllocationGranularity: ULONG;
  150. pLowestUserAddress: Pointer;
  151. pMmHighestUserAddress: Pointer;
  152. uKeActiveProcessors: ULONG;
  153. bKeNumberProcessors: Byte;
  154. bUnknown2: Byte;
  155. wUnknown3: Word;
  156. end;
  157. TSystem_Performance_Information = packed record
  158. liIdleTime: LARGE_INTEGER;
  159. dwSpare: array[0..75] of DWORD;
  160. end;
  161. TSystem_Time_Information = packed record
  162. liKeBootTime: LARGE_INTEGER;
  163. liKeSystemTime: LARGE_INTEGER;
  164. liExpTimeZoneBias: LARGE_INTEGER;
  165. uCurrentTimeZoneId: ULONG;
  166. dwReserved: DWORD;
  167. end;
  168. TP_NtQuerySystemInformation = function(InfoClass: DWORD; Buffer: Pointer;
  169. BufSize: DWORD; ReturnSize: PCardinal): DWORD; stdcall;
  170. var
  171. MP_GlobalMemoryStatusEx: TP_GlobalMemoryStatusEx = nil;
  172. MP_NtQuerySystemInformation: TP_NtQuerySystemInformation = nil;
  173. //-----------------------------------------------------------------------------
  174. // Various Global Procedures
  175. //-----------------------------------------------------------------------------
  176. function ShowFastMMUsageTracker: TfFastMMUsageTracker;
  177. begin
  178. Application.CreateForm(TfFastMMUsageTracker, Result);
  179. if Assigned(Result) then
  180. begin
  181. Result.RefreshSnapShot;
  182. Result.Show;
  183. end;
  184. end;
  185. function CardinalToStringFormatted(const ACardinal: Cardinal): string;
  186. begin
  187. Result := FormatFloat('#,##0', ACardinal);
  188. end;
  189. function Int64ToStringFormatted(const AInt64: Int64): string;
  190. begin
  191. Result := FormatFloat('#,##0', AInt64);
  192. end;
  193. function CardinalToKStringFormatted(const ACardinal: Cardinal): string;
  194. begin
  195. Result := FormatFloat('#,##0', ACardinal div 1024) + 'K';
  196. end;
  197. function Int64ToKStringFormatted(const AInt64: Int64): string;
  198. begin
  199. Result := FormatFloat('#,##0', AInt64 div 1024) + 'K';
  200. end;
  201. procedure CopyGridContentsToClipBoard(AStringGrid: TStringGrid);
  202. const
  203. TAB = Chr(VK_TAB);
  204. CRLF = #13#10;
  205. var
  206. LI_r, LI_c: Integer;
  207. LS_S: string;
  208. begin
  209. LS_S := '';
  210. for LI_r := 0 to AStringGrid.RowCount - 1 do
  211. begin
  212. for LI_c := 0 to AStringGrid.ColCount - 1 do
  213. begin
  214. LS_S := LS_S + AStringGrid.Cells[LI_c, LI_r];
  215. if LI_c < AStringGrid.ColCount - 1 then
  216. LS_S := LS_S + TAB;
  217. end;
  218. if LI_r < AStringGrid.RowCount - 1 then
  219. LS_S := LS_S + CRLF;
  220. end;
  221. ClipBoard.SetTextBuf(PChar(LS_S));
  222. end;
  223. //-----------------------------------------------------------------------------
  224. // Form TfFastMMUsageTracker
  225. //-----------------------------------------------------------------------------
  226. procedure TfFastMMUsageTracker.FormCreate(Sender: TObject);
  227. var
  228. LR_SystemInfo: TSystemInfo;
  229. begin
  230. pcUsageTracker.ActivePage := tsAllocation;
  231. GetSystemInfo(LR_SystemInfo);
  232. {Get the number of address space pages}
  233. if (Cardinal(LR_SystemInfo.lpMaximumApplicationAddress) and $80000000) = 0 then
  234. AddressSpacePageCount := 32768
  235. else
  236. AddressSpacePageCount := 65536;
  237. {Update the graph metricx}
  238. UpdateGraphMetrics;
  239. {Set up the StringGrid columns}
  240. with sgBlockStatistics do
  241. begin
  242. Cells[0, 0] := 'Block Size';
  243. Cells[1, 0] := '# Live Pointers';
  244. Cells[2, 0] := 'Live Size';
  245. Cells[3, 0] := 'Used Space';
  246. Cells[4, 0] := 'Efficiency';
  247. end;
  248. with sgVMDump do
  249. begin
  250. Cells[0, 0] := 'VM Block';
  251. Cells[1, 0] := 'Size';
  252. Cells[2, 0] := 'Type';
  253. Cells[3, 0] := 'State';
  254. Cells[4, 0] := 'EXE/DLL';
  255. end;
  256. end;
  257. procedure TfFastMMUsageTracker.FormClose(Sender: TObject; var Action: TCloseAction);
  258. begin
  259. Action := caFree;
  260. end;
  261. procedure TfFastMMUsageTracker.SortGrid(grid: TStringgrid; PB_Nummeric: Boolean; byColumn: Integer; ascending: Boolean);
  262. function CompareNumeric(const S1, S2: string): Integer;
  263. var
  264. LVal1, LVal2: Integer;
  265. begin
  266. begin
  267. LVal1 := StrToInt(S1);
  268. LVal2 := StrToInt(S2);
  269. if LVal1 = LVal2 then
  270. begin
  271. Result := 0;
  272. end
  273. else
  274. begin
  275. if LVal1 > LVal2 then
  276. Result := 1
  277. else
  278. Result := -1;
  279. end;
  280. end;
  281. end;
  282. procedure ExchangeGridRows(i, j: Integer);
  283. var
  284. k: Integer;
  285. begin
  286. for k := 0 to Grid.ColCount - 1 do
  287. Grid.Cols[k].Exchange(i, j);
  288. end;
  289. procedure QuickSortNummeric(L, R: Integer);
  290. var
  291. I, J: Integer;
  292. P: string;
  293. begin
  294. repeat
  295. I := L;
  296. J := R;
  297. P := Grid.Cells[byColumn, (L + R) shr 1];
  298. repeat
  299. while CompareNumeric(Grid.Cells[byColumn, I], P) < 0 do
  300. Inc(I);
  301. while CompareNumeric(Grid.Cells[byColumn, J], P) > 0 do
  302. Dec(J);
  303. if I <= J then
  304. begin
  305. if I <> J then
  306. ExchangeGridRows(I, J);
  307. Inc(I);
  308. Dec(J);
  309. end;
  310. until I > J;
  311. if L < J then
  312. QuickSortNummeric(L, J);
  313. L := I;
  314. until I >= R;
  315. end;
  316. procedure QuickSortString(L, R: Integer);
  317. var
  318. I, J: Integer;
  319. P: string;
  320. begin
  321. repeat
  322. I := L;
  323. J := R;
  324. P := Grid.Cells[byColumn, (L + R) shr 1];
  325. repeat
  326. while CompareText(Grid.Cells[byColumn, I], P) < 0 do
  327. Inc(I);
  328. while CompareText(Grid.Cells[byColumn, J], P) > 0 do
  329. Dec(J);
  330. if I <= J then
  331. begin
  332. if I <> J then
  333. ExchangeGridRows(I, J);
  334. Inc(I);
  335. Dec(J);
  336. end;
  337. until I > J;
  338. if L < J then
  339. QuickSortString(L, J);
  340. L := I;
  341. until I >= R;
  342. end;
  343. procedure InvertGrid;
  344. var
  345. i, j: Integer;
  346. begin
  347. i := Grid.Fixedrows;
  348. j := Grid.Rowcount - 1;
  349. while i < j do
  350. begin
  351. ExchangeGridRows(I, J);
  352. Inc(i);
  353. Dec(j);
  354. end;
  355. end;
  356. begin
  357. Screen.Cursor := crHourglass;
  358. Grid.Perform(WM_SETREDRAW, 0, 0);
  359. try
  360. if PB_Nummeric then
  361. QuickSortNummeric(Grid.FixedRows, Grid.Rowcount - 1)
  362. else
  363. QuickSortString(Grid.FixedRows, Grid.Rowcount - 1);
  364. if not Ascending then
  365. InvertGrid;
  366. finally
  367. Grid.Perform(WM_SETREDRAW, 1, 0);
  368. Grid.Refresh;
  369. Screen.Cursor := crDefault;
  370. end;
  371. end;
  372. procedure TfFastMMUsageTracker.HeaderClicked(AGrid: TStringgrid; const ACell: TGridCoord);
  373. var
  374. i: Integer;
  375. LNumericSort: Boolean;
  376. begin
  377. // The header cell stores a flag in the Objects property that signals the
  378. // current sort order of the grid column. A value of 0 shows no sort marker,
  379. // 1 means sorted ascending, -1 sorted descending
  380. // clear markers
  381. for i := AGrid.FixedCols to AGrid.ColCount - 1 do
  382. begin
  383. if Assigned(AGrid.Objects[i, 0]) and (i <> ACell.x) then
  384. begin
  385. AGrid.Objects[i, 0] := nil;
  386. TLocalStringGrid(AGrid).InvalidateCell(i, 0);
  387. end;
  388. end;
  389. // Sort grid on new column. If grid is currently sorted ascending on this
  390. // column we invert the sort direction, otherwise we sort it ascending.
  391. if ACell.X = 1 then
  392. LNumericSort := True
  393. else
  394. LNumericSort := False;
  395. if Integer(AGrid.Objects[ACell.x, ACell.y]) = 1 then
  396. begin
  397. SortGrid(AGrid, LNumericSort, ACell.x, False);
  398. AGrid.Objects[ACell.x, 0] := Pointer(-1);
  399. end
  400. else
  401. begin
  402. SortGrid(AGrid, LNumericSort, ACell.x, True);
  403. AGrid.Objects[ACell.x, 0] := Pointer(1);
  404. end;
  405. TLocalStringGrid(AGrid).InvalidateCell(ACell.x, ACell.y);
  406. end;
  407. procedure TfFastMMUsageTracker.UpdateGraphMetrics;
  408. begin
  409. if ChkSmallGraph.Checked then
  410. begin
  411. dgMemoryMap.DefaultColWidth := 4;
  412. dgMemoryMap.ColCount := 128;
  413. end
  414. else
  415. begin
  416. dgMemoryMap.DefaultColWidth := 8;
  417. dgMemoryMap.ColCount := 64;
  418. end;
  419. dgMemoryMap.DefaultRowHeight := dgMemoryMap.DefaultColWidth;
  420. dgMemoryMap.RowCount := AddressSpacePageCount div dgMemoryMap.ColCount;
  421. end;
  422. procedure TfFastMMUsageTracker.RefreshSnapShot;
  423. var
  424. LP_FreeVMList: TList;
  425. LU_MEM_FREE: DWord;
  426. LU_MEM_COMMIT: DWord;
  427. LU_MEM_RESERVE: DWord;
  428. LAllocatedSize, LTotalBlocks, LTotalAllocated, LTotalReserved,
  429. LPrevAllocatedSize, LPrevTotalBlocks, LPrevTotalAllocated, LPrevTotalReserved: Cardinal;
  430. procedure UpdateVMGraph(var AMemoryMap: TMemoryMapEx);
  431. var
  432. LInd, LIndTop, I1: Integer;
  433. LChunkState: TChunkStatusEx;
  434. LMBI: TMemoryBasicInformation;
  435. LA_Char: array[0..MAX_PATH] of Char;
  436. begin
  437. LInd := 0;
  438. repeat
  439. {If the chunk is not allocated by this MM, what is its status?}
  440. if AMemoryMap[LInd] = csExSysAllocated then
  441. begin
  442. {Get all the reserved memory blocks and windows allocated memory blocks, etc.}
  443. VirtualQuery(Pointer(LInd * 65536), LMBI, SizeOf(LMBI));
  444. if LMBI.State = MEM_COMMIT then
  445. begin
  446. if (GetModuleFileName(DWord(LMBI.AllocationBase), LA_Char, MAX_PATH) <> 0) then
  447. begin
  448. if DWord(LMBI.AllocationBase) = SysInit.HInstance then
  449. LChunkState := csExSysExe
  450. else
  451. LChunkState := csExSysDLL;
  452. end
  453. else
  454. begin
  455. LChunkState := csExSysAllocated;
  456. end;
  457. if LMBI.RegionSize > 65536 then
  458. begin
  459. LIndTop := (Cardinal(LMBI.BaseAddress) + Cardinal(LMBI.RegionSize)) div 65536;
  460. // Fill up multiple tables
  461. for I1 := LInd to LIndTop do
  462. AMemoryMap[I1] := LChunkState;
  463. LInd := LIndTop;
  464. end
  465. else
  466. begin
  467. AMemoryMap[LInd] := LChunkState;
  468. end;
  469. end
  470. end;
  471. Inc(LInd);
  472. until LInd >= AddressSpacePageCount;
  473. end;
  474. procedure UpdateVMDump;
  475. var
  476. LP_Base: PByte;
  477. LR_Info: TMemoryBasicInformation;
  478. LU_rv: DWORD;
  479. LI_I: Integer;
  480. LA_Char: array[0..MAX_PATH] of Char;
  481. begin
  482. LP_Base := nil;
  483. LU_rv := VirtualQuery(LP_Base, LR_Info, sizeof(LR_Info));
  484. LI_I := 1;
  485. while LU_rv = sizeof(LR_Info) do
  486. begin
  487. with sgVMDump do
  488. begin
  489. Cells[0, LI_I] := IntToHex(Integer(LR_Info.BaseAddress), 8);
  490. Cells[1, LI_I] := IntToStr(LR_Info.RegionSize);
  491. Cells[3, LI_I] := IntToHex(Integer(LR_Info.Protect), 8);
  492. case LR_Info.State of
  493. MEM_Commit:
  494. begin
  495. LU_MEM_COMMIT := LU_MEM_COMMIT + LR_Info.RegionSize;
  496. if (GetModuleFileName(dword(LR_Info.AllocationBase), LA_Char, MAX_PATH) <> 0) then
  497. begin
  498. if DWord(LR_Info.AllocationBase) = SysInit.HInstance then
  499. Cells[2, LI_I] := 'Exe'
  500. else
  501. Cells[2, LI_I] := 'DLL';
  502. Cells[4, LI_I] := ExtractFileName(LA_Char);
  503. end
  504. else
  505. begin
  506. Cells[4, LI_I] := '';
  507. Cells[2, LI_I] := 'Commited';
  508. end;
  509. end;
  510. MEM_RESERVE:
  511. begin
  512. LU_MEM_RESERVE := LU_MEM_RESERVE + LR_Info.RegionSize;
  513. Cells[2, LI_I] := 'Reserved';
  514. Cells[4, LI_I] := '';
  515. end;
  516. MEM_FREE:
  517. begin
  518. LP_FreeVMList.Add(Pointer(LR_Info.RegionSize));
  519. LU_MEM_FREE := LU_MEM_FREE + Lr_Info.RegionSize;
  520. Cells[2, LI_I] := 'Free';
  521. Cells[4, LI_I] := '';
  522. end;
  523. end;
  524. Inc(LP_Base, LR_Info.RegionSize);
  525. LU_rv := VirtualQuery(LP_Base, LR_Info, sizeof(LR_Info));
  526. Inc(LI_I);
  527. end;
  528. end;
  529. sgVMDump.RowCount := LI_I;
  530. end;
  531. procedure UpdateFastMM4Data;
  532. var
  533. LInd: Integer;
  534. LU_StateLength: Cardinal;
  535. LPrevSBState, LSBState: ^TSmallBlockTypeState;
  536. procedure UpdateBlockStatistics(c, r, current, prev: Integer);
  537. var
  538. s : string;
  539. begin
  540. s := IntToStr(current);
  541. if current > prev then
  542. s := s + ' (+' + IntToStr(current - prev) + ')'
  543. else if current < prev then
  544. s := s + ' (-' + IntToStr(prev - current) + ')';
  545. sgBlockStatistics.Cells[c, r] := s;
  546. sgBlockStatistics.Objects[c, r] := Pointer(current - prev);
  547. end;
  548. begin
  549. LU_StateLength := Length(FMemoryManagerState.SmallBlockTypeStates);
  550. {Set up the row count}
  551. sgBlockStatistics.RowCount := LU_StateLength + 4;
  552. sgBlockStatistics.Cells[0, LU_StateLength + 1] := 'Medium Blocks';
  553. sgBlockStatistics.Cells[0, LU_StateLength + 2] := 'Large Blocks';
  554. sgBlockStatistics.Cells[0, LU_StateLength + 3] := 'Overall';
  555. for LInd := 0 to High(FMemoryManagerState.SmallBlockTypeStates) do
  556. begin
  557. sgBlockStatistics.Cells[0, LInd + 1] :=
  558. IntToStr(FMemoryManagerState.SmallBlockTypeStates[LInd].InternalBlockSize)
  559. + '(' + IntToStr(FMemoryManagerState.SmallBlockTypeStates[LInd].UseableBlockSize) + ')';
  560. end;
  561. {Set the texts inside the results string grid}
  562. for LInd := 0 to High(FMemoryManagerState.SmallBlockTypeStates) do
  563. begin
  564. LPrevSBState := @FPrevMemoryManagerState.SmallBlockTypeStates[LInd];
  565. LSBState := @FMemoryManagerState.SmallBlockTypeStates[LInd];
  566. UpdateBlockStatistics(1, LInd + 1, LSBState.AllocatedBlockCount, LPrevSBState.AllocatedBlockCount);
  567. Inc(LTotalBlocks, LSBState.AllocatedBlockCount);
  568. Inc(LPrevTotalBlocks, LPrevSBState.AllocatedBlockCount);
  569. LAllocatedSize := LSBState.AllocatedBlockCount * LSBState.UseableBlockSize;
  570. LPrevAllocatedSize := LPrevSBState.AllocatedBlockCount * LPrevSBState.UseableBlockSize;
  571. UpdateBlockStatistics(2, LInd + 1, LAllocatedSize, LPrevAllocatedSize);
  572. Inc(LTotalAllocated, LAllocatedSize);
  573. Inc(LPrevTotalAllocated, LPrevAllocatedSize);
  574. UpdateBlockStatistics(3, LInd + 1, LSBState.ReservedAddressSpace, LPrevSBState.ReservedAddressSpace);
  575. Inc(LTotalReserved, LSBState.ReservedAddressSpace);
  576. Inc(LPrevTotalReserved, LPrevSBState.ReservedAddressSpace);
  577. if LSBState.ReservedAddressSpace > 0 then
  578. sgBlockStatistics.Cells[4, LInd + 1] := FormatFloat('0.##%', LAllocatedSize / LSBState.ReservedAddressSpace * 100)
  579. else
  580. sgBlockStatistics.Cells[4, LInd + 1] := 'N/A';
  581. end;
  582. {-----------Medium blocks---------}
  583. LInd := length(FMemoryManagerState.SmallBlockTypeStates) + 1;
  584. UpdateBlockStatistics(1, LInd, FMemoryManagerState.AllocatedMediumBlockCount, FPrevMemoryManagerState.AllocatedMediumBlockCount);
  585. Inc(LTotalBlocks, FMemoryManagerState.AllocatedMediumBlockCount);
  586. Inc(LPrevTotalBlocks, FPrevMemoryManagerState.AllocatedMediumBlockCount);
  587. UpdateBlockStatistics(2, LInd, FMemoryManagerState.TotalAllocatedMediumBlockSize, FPrevMemoryManagerState.TotalAllocatedMediumBlockSize);
  588. Inc(LTotalAllocated, FMemoryManagerState.TotalAllocatedMediumBlockSize);
  589. Inc(LPrevTotalAllocated, FPrevMemoryManagerState.TotalAllocatedMediumBlockSize);
  590. UpdateBlockStatistics(3, LInd, FMemoryManagerState.ReservedMediumBlockAddressSpace, FPrevMemoryManagerState.ReservedMediumBlockAddressSpace);
  591. Inc(LTotalReserved, FMemoryManagerState.ReservedMediumBlockAddressSpace);
  592. Inc(LPrevTotalReserved, FPrevMemoryManagerState.ReservedMediumBlockAddressSpace);
  593. if FMemoryManagerState.ReservedMediumBlockAddressSpace > 0 then
  594. sgBlockStatistics.Cells[4, LInd] := FormatFloat('0.##%', FMemoryManagerState.TotalAllocatedMediumBlockSize / FMemoryManagerState.ReservedMediumBlockAddressSpace * 100)
  595. else
  596. sgBlockStatistics.Cells[4, LInd] := 'N/A';
  597. {----------Large blocks----------}
  598. LInd := Length(FMemoryManagerState.SmallBlockTypeStates) + 2;
  599. UpdateBlockStatistics(1, LInd, FMemoryManagerState.AllocatedLargeBlockCount, FPrevMemoryManagerState.AllocatedLargeBlockCount);
  600. Inc(LTotalBlocks, FMemoryManagerState.AllocatedLargeBlockCount);
  601. Inc(LPrevTotalBlocks, FPrevMemoryManagerState.AllocatedLargeBlockCount);
  602. UpdateBlockStatistics(2, LInd, FMemoryManagerState.TotalAllocatedLargeBlockSize, FPrevMemoryManagerState.TotalAllocatedLargeBlockSize);
  603. Inc(LTotalAllocated, FMemoryManagerState.TotalAllocatedLargeBlockSize);
  604. Inc(LPrevTotalAllocated, FPrevMemoryManagerState.TotalAllocatedLargeBlockSize);
  605. UpdateBlockStatistics(3, LInd, FMemoryManagerState.ReservedLargeBlockAddressSpace, FPrevMemoryManagerState.ReservedLargeBlockAddressSpace);
  606. Inc(LTotalReserved, FMemoryManagerState.ReservedLargeBlockAddressSpace);
  607. Inc(LPrevTotalReserved, FPrevMemoryManagerState.ReservedLargeBlockAddressSpace);
  608. if FMemoryManagerState.ReservedLargeBlockAddressSpace > 0 then
  609. sgBlockStatistics.Cells[4, LInd] := FormatFloat('0.##%', FMemoryManagerState.TotalAllocatedLargeBlockSize / FMemoryManagerState.ReservedLargeBlockAddressSpace * 100)
  610. else
  611. sgBlockStatistics.Cells[4, LInd] := 'N/A';
  612. {-----------Overall--------------}
  613. LInd := Length(FMemoryManagerState.SmallBlockTypeStates) + 3;
  614. UpdateBlockStatistics(1, Lind, LTotalBlocks, LPrevTotalBlocks);
  615. UpdateBlockStatistics(2, Lind, LTotalAllocated, LPrevTotalAllocated);
  616. UpdateBlockStatistics(3, Lind, LTotalReserved, LPrevTotalReserved);
  617. if LTotalReserved > 0 then
  618. sgBlockStatistics.Cells[4, LInd] := FormatFloat('0.##%', LTotalAllocated / LTotalReserved * 100)
  619. else
  620. sgBlockStatistics.Cells[4, LInd] := 'N/A';
  621. end;
  622. procedure UpdateStatisticsData;
  623. function LocSort(P1, P2: Pointer): Integer;
  624. begin
  625. if Cardinal(P1) = Cardinal(P2) then
  626. Result := 0
  627. else
  628. begin
  629. if Cardinal(P1) > Cardinal(P2) then
  630. Result := -1
  631. else
  632. Result := 1;
  633. end;
  634. end;
  635. const
  636. CI_MaxFreeBlocksList = 9;
  637. var
  638. LR_SystemInfo: TSystemInfo;
  639. LR_GlobalMemoryStatus: TMemoryStatus;
  640. LR_GlobalMemoryStatusEx: TMemoryStatusEx;
  641. LR_ProcessMemoryCounters: TProcessMemoryCounters;
  642. LR_SysBaseInfo: TSystem_Basic_Information;
  643. LU_MinQuota: {$if CompilerVersion >= 23}NativeUInt{$else}Cardinal{$ifend};
  644. LU_MaxQuota: {$if CompilerVersion >= 23}NativeUInt{$else}Cardinal{$ifend};
  645. LI_I: Integer;
  646. LI_Max: Integer;
  647. begin
  648. mVMStatistics.Lines.BeginUpdate;
  649. try
  650. mVMStatistics.Clear;
  651. LU_MinQuota := 0;
  652. LU_MaxQuota := 0;
  653. if Assigned(MP_GlobalMemoryStatusEx) then
  654. begin
  655. ZeroMemory(@LR_GlobalMemoryStatusEx, SizeOf(TMemoryStatusEx));
  656. LR_GlobalMemoryStatusEx.dwLength := SizeOf(TMemoryStatusEx);
  657. if not MP_GlobalMemoryStatusEx(LR_GlobalMemoryStatusEx) then
  658. begin
  659. mVMStatistics.Lines.Add('GlobalMemoryStatusEx err: ' + SysErrorMessage(GetLastError));
  660. end;
  661. end
  662. else
  663. begin
  664. LR_GlobalMemoryStatus.dwLength := SizeOf(TMemoryStatus);
  665. GlobalMemoryStatus(LR_GlobalMemoryStatus);
  666. end;
  667. LP_FreeVMList.Sort(@LocSort);
  668. GetProcessWorkingSetSize(GetCurrentProcess, LU_MinQuota, LU_MaxQuota);
  669. GetSystemInfo(LR_SystemInfo);
  670. with mVMStatistics.Lines do
  671. begin
  672. Add('System Info:');
  673. Add('------------');
  674. Add('Processor Count = ' + IntToStr(LR_SystemInfo.dwNumberOfProcessors));
  675. Add('Allocation Granularity = ' + IntToStr(LR_SystemInfo.dwAllocationGranularity));
  676. if Assigned(MP_GlobalMemoryStatusEx) then
  677. begin
  678. with LR_GlobalMemoryStatusEx do
  679. begin
  680. Add('Available Physical Memory = ' + Int64ToKStringFormatted(ullAvailPhys));
  681. Add('Total Physical Memory = ' + Int64ToKStringFormatted(ullTotalPhys));
  682. Add('Available Virtual Memory = ' + Int64ToKStringFormatted(ullAvailVirtual));
  683. Add('Total Virtual Memory = ' + Int64ToKStringFormatted(ullTotalVirtual));
  684. Add('Total Virtual Extended Memory = ' + Int64ToKStringFormatted(ullAvailExtendedVirtual));
  685. end;
  686. end
  687. else
  688. begin
  689. with LR_GlobalMemoryStatus do
  690. begin
  691. Add('Available Physical Memory = ' + CardinalToKStringFormatted(dwAvailPhys));
  692. Add('Total Physical Memory = ' + CardinalToKStringFormatted(dwTotalPhys));
  693. Add('Available Virtual Memory = ' + CardinalToKStringFormatted(dwAvailVirtual));
  694. Add('Total Virtual Memory = ' + CardinalToKStringFormatted(dwTotalVirtual));
  695. end;
  696. end;
  697. if Assigned(MP_NtQuerySystemInformation) then
  698. begin
  699. if MP_NtQuerySystemInformation(SystemBasicInformation, @LR_SysBaseInfo, SizeOf(LR_SysBaseInfo), nil) = 0 then
  700. begin
  701. with LR_SysBaseInfo do begin
  702. Add('Maximum Increment = ' + CardinalToKStringFormatted(uKeMaximumIncrement));
  703. Add('Page Size = ' + CardinalToKStringFormatted(uPageSize));
  704. Add('Number of Physical Pages = ' + CardinalToKStringFormatted(uMmNumberOfPhysicalPages));
  705. Add('Lowest Physical Page = ' + CardinalToStringFormatted(uMmLowestPhysicalPage));
  706. Add('Highest Physical Page = ' + CardinalToKStringFormatted(uMmHighestPhysicalPage));
  707. end;
  708. end;
  709. end;
  710. // same as GetProcessMemoryInfo & NtQuerySystemInformation (SystemBasicInformation
  711. // The working set is the amount of memory physically mapped to the process context at a given
  712. // time. Memory in the paged pool is system memory that can be transferred to the paging file
  713. // on disk (paged) when it is not being used. Memory in the nonpaged pool is system memory
  714. // that cannot be paged to disk as long as the corresponding objects are allocated. The pagefile
  715. // usage represents how much memory is set aside for the process in the system paging file.
  716. // When memory usage is too high, the virtual memory manager pages selected memory to disk.
  717. // When a thread needs a page that is not in memory, the memory manager reloads it from the
  718. // paging file.
  719. if GetProcessMemoryInfo(GetCurrentProcess, @LR_ProcessMemoryCounters, SizeOf(LR_ProcessMemoryCounters)) then
  720. begin
  721. with LR_ProcessMemoryCounters do
  722. begin
  723. Add('Page Fault Count = ' + CardinalToKStringFormatted(PageFaultCount));
  724. Add('Peak Working Set Size = ' + CardinalToKStringFormatted(PeakWorkingSetSize));
  725. Add('Working Set Size = ' + CardinalToKStringFormatted(WorkingSetSize));
  726. Add('Quota Peak Paged Pool Usage = ' + CardinalToKStringFormatted(QuotaPeakPagedPoolUsage));
  727. Add('Quota Paged Pool Usage = ' + CardinalToStringFormatted(QuotaPagedPoolUsage));
  728. Add('Quota Peak Non-Paged Pool Usage = ' + CardinalToStringFormatted(QuotaPeakNonPagedPoolUsage));
  729. Add('Quota Non-Paged Pool Usage = ' + CardinalToStringFormatted(QuotaNonPagedPoolUsage));
  730. Add('Pagefile Usage = ' + CardinalToKStringFormatted(PagefileUsage));
  731. Add('Peak Pagefile Usage = ' + CardinalToKStringFormatted(PeakPagefileUsage));
  732. end;
  733. end;
  734. Add('');
  735. Add('Process Info: PID (' + IntToStr(GetCurrentProcessId) + ')');
  736. Add('------------------------');
  737. Add('Minimum Address = ' + CardinalToStringFormatted(Cardinal(LR_SystemInfo.lpMinimumApplicationAddress)));
  738. Add('Maximum VM Address = ' + CardinalToKStringFormatted(Cardinal(LR_SystemInfo.lpMaximumApplicationAddress)));
  739. Add('Page Protection & Commit Size = ' + IntToStr(LR_SystemInfo.dWPageSize));
  740. Add('');
  741. Add('Quota info:');
  742. Add('-----------');
  743. Add('Minimum Quota = ' + CardinalToStringFormatted(LU_MinQuota));
  744. Add('Maximum Quota = ' + CardinalToStringFormatted(LU_MaxQuota));
  745. Add('');
  746. Add('VM Info:');
  747. Add('--------');
  748. Add('Total Free = ' + CardinalToKStringFormatted(LU_MEM_FREE));
  749. Add('Total Reserve = ' + CardinalToKStringFormatted(LU_MEM_RESERVE));
  750. Add('Total Commit = ' + CardinalToKStringFormatted(LU_MEM_COMMIT));
  751. if LP_FreeVMList.Count > CI_MaxFreeBlocksList then
  752. LI_Max := CI_MaxFreeBlocksList - 1
  753. else
  754. LI_Max := LP_FreeVMList.Count - 1;
  755. for LI_I := 0 to LI_Max do
  756. begin
  757. Add('Largest Free Block ' + IntToStr(LI_I + 1) + '. = ' + CardinalToKStringFormatted(Cardinal(LP_FreeVMList.List[LI_I])));
  758. end;
  759. Add('');
  760. Add('FastMM4 Info:');
  761. Add('-------------');
  762. Add('Total Blocks = ' + CardinalToStringFormatted(LTotalBlocks));
  763. Add('Total Allocated = ' + CardinalToStringFormatted(LTotalAllocated));
  764. Add('Total Reserved = ' + CardinalToStringFormatted(LTotalReserved));
  765. end;
  766. finally
  767. mVMStatistics.Lines.EndUpdate;
  768. end;
  769. end;
  770. var
  771. Save_Cursor: TCursor;
  772. begin
  773. if SizeOf(TMemoryMap) <> SizeOf(TMemoryMapEx) then
  774. begin
  775. Showmessage('Internal implementation error');
  776. Exit;
  777. end;
  778. LU_MEM_FREE := 0;
  779. LU_MEM_COMMIT := 0;
  780. LU_MEM_RESERVE := 0;
  781. LTotalBlocks := 0;
  782. LTotalAllocated := 0;
  783. LTotalReserved := 0;
  784. LPrevTotalBlocks := 0;
  785. LPrevTotalAllocated := 0;
  786. LPrevTotalReserved := 0;
  787. // Set hourglass cursor
  788. Save_Cursor := Screen.Cursor;
  789. Screen.Cursor := crHourGlass;
  790. LP_FreeVMList := TList.Create;
  791. try
  792. // retrieve FastMM4 info
  793. GetMemoryManagerState(FMemoryManagerState);
  794. GetMemoryMap(TMemoryMap(FMemoryMapEx));
  795. // Update FastMM4 Graph with EXE & DLL locations
  796. UpdateVMGraph(FMemoryMapEx);
  797. // VM dump
  798. UpdateVMDump;
  799. // FastMM4 data
  800. UpdateFastMM4Data;
  801. // General Information
  802. UpdateStatisticsData;
  803. // Screen updates
  804. dgMemoryMap.Invalidate;
  805. FPrevMemoryManagerState := FMemoryManagerState;
  806. finally
  807. FreeAndNil(LP_FreeVMList);
  808. Screen.Cursor := Save_Cursor;
  809. end;
  810. end;
  811. procedure TfFastMMUsageTracker.sgBlockStatisticsDrawCell(Sender: TObject;
  812. ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
  813. var
  814. d: integer;
  815. y: integer;
  816. s: string;
  817. LOldColour, LColour: TColor;
  818. begin
  819. d := Integer(sgBlockStatistics.Objects[ACol, ARow]);
  820. if d <> 0 then
  821. begin
  822. LOldColour := sgBlockStatistics.Canvas.Brush.Color;
  823. if d < 0 then
  824. LColour := clLime
  825. else
  826. LColour := clRed;
  827. sgBlockStatistics.Canvas.Brush.Color := LColour;
  828. sgBlockStatistics.Canvas.Font.Color := clWindowText;
  829. s := sgBlockStatistics.Cells[ACol, ARow];
  830. y := sgBlockStatistics.Canvas.TextHeight(s);
  831. y := ((Rect.Bottom - Rect.Top) - y) div 2;
  832. sgBlockStatistics.Canvas.TextRect(Rect, Rect.Left + 2, Rect.top + y, s);
  833. sgBlockStatistics.Canvas.Brush.Color := LOldColour;
  834. end;
  835. end;
  836. procedure TfFastMMUsageTracker.tTimerTimer(Sender: TObject);
  837. begin
  838. tTimer.Enabled := False;
  839. try
  840. RefreshSnapShot;
  841. finally
  842. tTimer.Enabled := True;
  843. end;
  844. end;
  845. procedure TfFastMMUsageTracker.bCloseClick(Sender: TObject);
  846. begin
  847. Close;
  848. end;
  849. procedure TfFastMMUsageTracker.dgMemoryMapDrawCell(Sender: TObject; ACol,
  850. ARow: Integer; Rect: TRect; State: TGridDrawState);
  851. var
  852. LChunkIndex: integer;
  853. LChunkColour: TColor;
  854. begin
  855. {Get the chunk index}
  856. LChunkIndex := ARow * dgMemoryMap.ColCount + ACol;
  857. {Get the correct colour}
  858. case FMemoryMapEx[LChunkIndex] of
  859. csExAllocated:
  860. begin
  861. LChunkColour := $9090FF;
  862. end;
  863. csExReserved:
  864. begin
  865. LChunkColour := $90F090;
  866. end;
  867. csExSysAllocated:
  868. begin
  869. LChunkColour := $707070;
  870. end;
  871. csExSysExe:
  872. begin
  873. LChunkColour := clRed;
  874. end;
  875. csExSysDLL:
  876. begin
  877. LChunkColour := clBlue;
  878. end;
  879. csExSysReserved:
  880. begin
  881. LChunkColour := $C0C0C0;
  882. end
  883. else
  884. begin
  885. {ExUnallocated}
  886. LChunkColour := $FFFFFF;
  887. end;
  888. end;
  889. {Draw the chunk background}
  890. dgMemoryMap.Canvas.Brush.Color := LChunkColour;
  891. if State = [] then
  892. dgMemoryMap.Canvas.FillRect(Rect)
  893. else
  894. dgMemoryMap.Canvas.Rectangle(Rect);
  895. end;
  896. procedure TfFastMMUsageTracker.dgMemoryMapSelectCell(Sender: TObject; ACol,
  897. ARow: Integer; var CanSelect: Boolean);
  898. var
  899. LChunkIndex: Cardinal;
  900. LMBI: TMemoryBasicInformation;
  901. LA_Char: array[0..MAX_PATH] of char;
  902. begin
  903. eDLLName.Text := '';
  904. LChunkIndex := ARow * dgMemoryMap.ColCount + ACol;
  905. eAddress.Text := Format('$%0.8x', [LChunkIndex shl 16]);
  906. case FMemoryMapEx[LChunkIndex] of
  907. csExAllocated:
  908. begin
  909. eState.Text := 'FastMM Allocated';
  910. end;
  911. csExReserved:
  912. begin
  913. eState.Text := 'FastMM Reserved';
  914. end;
  915. csExSysAllocated:
  916. begin
  917. eState.Text := 'System Allocated';
  918. end;
  919. csExSysExe:
  920. begin
  921. eState.Text := 'System Exe';
  922. VirtualQuery(Pointer(LChunkIndex shl 16), LMBI, SizeOf(LMBI));
  923. if (GetModuleFileName(dword(LMBI.AllocationBase), LA_Char, MAX_PATH) <> 0) then
  924. begin
  925. eDLLName.Text := LA_Char;
  926. end;
  927. end;
  928. csExSysDLL:
  929. begin
  930. eState.Text := 'System/User DLL';
  931. VirtualQuery(Pointer(LChunkIndex shl 16), LMBI, SizeOf(LMBI));
  932. if (GetModuleFileName(dword(LMBI.AllocationBase), LA_Char, MAX_PATH) <> 0) then
  933. begin
  934. eDLLName.Text := LA_Char;
  935. end;
  936. end;
  937. csExSysReserved:
  938. begin
  939. eState.Text := 'System Reserved';
  940. end
  941. else
  942. begin
  943. {ExUnallocated}
  944. eState.Text := 'Free';
  945. end;
  946. end;
  947. end;
  948. procedure TfFastMMUsageTracker.bUpdateClick(Sender: TObject);
  949. begin
  950. RefreshSnapShot;
  951. end;
  952. procedure TfFastMMUsageTracker.ChkAutoUpdateClick(Sender: TObject);
  953. begin
  954. tTimer.Enabled := ChkAutoUpdate.Checked;
  955. end;
  956. procedure TfFastMMUsageTracker.ChkSmallGraphClick(Sender: TObject);
  957. begin
  958. UpdateGraphMetrics;
  959. dgMemoryMap.Invalidate;
  960. dgMemoryMap.SetFocus;
  961. end;
  962. procedure TfFastMMUsageTracker.sgVMDumpMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  963. begin
  964. if (Button = mbLeft) and (Shift = [ssLeft]) then
  965. begin
  966. (Sender as TStringgrid).MouseToCell(X, Y, OR_VMDumpDownCell.X, OR_VMDumpDownCell.Y);
  967. end
  968. else
  969. begin
  970. OR_VMDumpDownCell.X := 0;
  971. OR_VMDumpDownCell.Y := 0;
  972. end;
  973. end;
  974. procedure TfFastMMUsageTracker.sgVMDumpMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  975. var
  976. p: TGridCoord;
  977. LGrid: TStringgrid;
  978. begin
  979. LGrid := Sender as TStringGrid;
  980. if (Button = mbLeft) and (Shift = []) then
  981. begin
  982. LGrid.MouseToCell(X, Y, p.X, p.Y);
  983. if CompareMem(@p, @OR_VMDumpDownCell, sizeof(p))
  984. and (p.Y < LGrid.FixedRows)
  985. and (p.X >= LGrid.FixedCols) then
  986. begin
  987. HeaderClicked(LGrid, p);
  988. end;
  989. end;
  990. OR_VMDumpDownCell.X := 0;
  991. OR_VMDumpDownCell.Y := 0;
  992. end;
  993. procedure TfFastMMUsageTracker.sgVMDumpDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
  994. var
  995. LGrid: TStringgrid;
  996. LMarker: Char;
  997. begin
  998. LGrid := Sender as TStringgrid;
  999. // paint the sort marker on header columns
  1000. if (ACol >= LGrid.FixedCols) and (aRow = 0) then
  1001. begin
  1002. if Assigned(LGrid.Objects[aCol, aRow]) then
  1003. begin
  1004. if Integer(LGrid.Objects[aCol, aRow]) > 0 then
  1005. LMarker := 't' // up wedge in Marlett font
  1006. else
  1007. LMarker := 'u'; // down wedge in Marlett font
  1008. with LGrid.canvas do
  1009. begin
  1010. Font.Name := 'Marlett';
  1011. Font.Charset := SYMBOL_CHARSET;
  1012. Font.Size := 12;
  1013. TextOut(Rect.Right - TextWidth(LMarker), Rect.Top, LMarker);
  1014. Font := LGrid.font;
  1015. end;
  1016. end;
  1017. end;
  1018. end;
  1019. procedure TfFastMMUsageTracker.siMM4AllocationCopyAlltoClipboardClick(Sender: TObject);
  1020. begin
  1021. CopyGridContentsToClipBoard(sgBlockStatistics);
  1022. end;
  1023. procedure TfFastMMUsageTracker.miVMDumpCopyAlltoClipboardClick(Sender: TObject);
  1024. begin
  1025. CopyGridContentsToClipBoard(sgVMDump);
  1026. end;
  1027. procedure TfFastMMUsageTracker.miGeneralInformationCopyAlltoClipboardClick(Sender: TObject);
  1028. begin
  1029. with mVMStatistics do
  1030. begin
  1031. Lines.BeginUpdate;
  1032. try
  1033. SelectAll;
  1034. CopyToClipboard;
  1035. SelStart := 0;
  1036. finally
  1037. Lines.EndUpdate;
  1038. end;
  1039. end;
  1040. end;
  1041. procedure ModuleInit;
  1042. begin
  1043. if Win32Platform = VER_PLATFORM_WIN32_NT then
  1044. begin
  1045. MP_GlobalMemoryStatusEx := TP_GlobalMemoryStatusEx(
  1046. GetProcAddress(GetModuleHandle(kernel32), 'GlobalMemoryStatusEx'));
  1047. MP_NtQuerySystemInformation := TP_NtQuerySystemInformation(
  1048. GetProcAddress(GetModuleHandle('ntdll.dll'), 'NtQuerySystemInformation'));
  1049. end;
  1050. end;
  1051. initialization
  1052. ModuleInit;
  1053. end.