Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

717 lines
13 KiB

  1. /*++
  2. Copyright (c) 1999-1999 Microsoft Corporation
  3. Module Name:
  4. vm.cxx
  5. Abstract:
  6. This module contains an NTSD debugger extension for dumping various
  7. virtual memory statistics.
  8. Author:
  9. Keith Moore (keithmo) 15-Jan-1999
  10. Revision History:
  11. --*/
  12. #include "inetdbgp.h"
  13. //
  14. // Private constants.
  15. //
  16. #define SMALL_REGION (64 * 1024)
  17. #define MEDIUM_REGION (1 * 1024 * 1024)
  18. #define IS_SMALL(c) ((c) <= SMALL_REGION)
  19. #define IS_MEDIUM(c) (((c) > SMALL_REGION) && ((c) <= MEDIUM_REGION))
  20. #define IS_LARGE(c) ((c) > MEDIUM_REGION)
  21. #define PRINTF_FORMAT "%-7s %*s %*s %*s %*s\n"
  22. #define CCH_ULONG_COMMAS sizeof("4,294,967,296")
  23. //
  24. // Private types.
  25. //
  26. typedef struct _INDIVIDUAL_STAT
  27. {
  28. SIZE_T MinimumSize;
  29. SIZE_T MaximumSize;
  30. SIZE_T TotalSize;
  31. SIZE_T BlockCount;
  32. } INDIVIDUAL_STAT, *PINDIVIDUAL_STAT;
  33. typedef struct _VM_STATS
  34. {
  35. INDIVIDUAL_STAT Summary;
  36. INDIVIDUAL_STAT Small;
  37. INDIVIDUAL_STAT Medium;
  38. INDIVIDUAL_STAT Large;
  39. } VM_STATS, *PVM_STATS;
  40. typedef struct PROTECT_MASK
  41. {
  42. DWORD Bit;
  43. PSTR Name;
  44. } PROTECT_MASK, *PPROTECT_MASK;
  45. //
  46. // Private globals.
  47. //
  48. PROTECT_MASK ProtectMasks[] =
  49. {
  50. {
  51. PAGE_NOACCESS,
  52. "NA"
  53. },
  54. {
  55. PAGE_NOCACHE,
  56. "NC"
  57. },
  58. {
  59. PAGE_GUARD,
  60. "G"
  61. },
  62. {
  63. PAGE_READONLY,
  64. "Rd"
  65. },
  66. {
  67. PAGE_READWRITE,
  68. "RdWr"
  69. },
  70. {
  71. PAGE_WRITECOPY,
  72. "WrCp"
  73. },
  74. {
  75. PAGE_EXECUTE,
  76. "Ex"
  77. },
  78. {
  79. PAGE_EXECUTE_READ,
  80. "ExRd"
  81. },
  82. {
  83. PAGE_EXECUTE_READWRITE,
  84. "ExRdWr"
  85. },
  86. {
  87. PAGE_EXECUTE_WRITECOPY,
  88. "ExWrCp"
  89. }
  90. };
  91. #define NUM_PROTECT_MASKS (sizeof(ProtectMasks) / sizeof(ProtectMasks[0]))
  92. //
  93. // Private functions.
  94. //
  95. PSTR
  96. ULongLongToString(
  97. IN ULONGLONG Value,
  98. OUT PSTR Buffer
  99. )
  100. {
  101. PSTR p1;
  102. PSTR p2;
  103. CHAR ch;
  104. INT digit;
  105. INT count;
  106. BOOL needComma;
  107. INT length;
  108. //
  109. // Handling zero specially makes everything else a bit easier.
  110. //
  111. if( Value == 0 ) {
  112. Buffer[0] = '0';
  113. Buffer[1] = '\0';
  114. return Buffer;
  115. }
  116. //
  117. // Pull the least signifigant digits off the value and store them
  118. // into the buffer. Note that this will store the digits in the
  119. // reverse order.
  120. //
  121. p1 = p2 = Buffer;
  122. count = 3;
  123. needComma = FALSE;
  124. while( Value != 0 ) {
  125. if( needComma ) {
  126. *p1++ = ',';
  127. needComma = FALSE;
  128. }
  129. digit = (INT)( Value % 10 );
  130. Value = Value / 10;
  131. *p1++ = '0' + digit;
  132. count--;
  133. if( count == 0 ) {
  134. count = 3;
  135. needComma = TRUE;
  136. }
  137. }
  138. length = DIFF(p1 - Buffer);
  139. //
  140. // Reverse the digits in the buffer.
  141. //
  142. *p1-- = '\0';
  143. while( p1 > p2 ) {
  144. ch = *p1;
  145. *p1 = *p2;
  146. *p2 = ch;
  147. p2++;
  148. p1--;
  149. }
  150. return Buffer;
  151. } // ULongLongToString
  152. VOID
  153. InitVmStats(
  154. OUT PVM_STATS Stats
  155. )
  156. {
  157. ZeroMemory( Stats, sizeof(*Stats) );
  158. Stats->Summary.MinimumSize = (SIZE_T)-1L;
  159. Stats->Small.MinimumSize = (SIZE_T)-1L;
  160. Stats->Medium.MinimumSize = (SIZE_T)-1L;
  161. Stats->Large.MinimumSize = (SIZE_T)-1L;
  162. } // InitVmStats
  163. VOID
  164. UpdateIndividualStat(
  165. IN OUT PINDIVIDUAL_STAT Stat,
  166. IN SIZE_T BlockSize
  167. )
  168. {
  169. Stat->BlockCount++;
  170. Stat->TotalSize += BlockSize;
  171. if( BlockSize > Stat->MaximumSize ) {
  172. Stat->MaximumSize = BlockSize;
  173. }
  174. if( BlockSize < Stat->MinimumSize ) {
  175. Stat->MinimumSize = BlockSize;
  176. }
  177. } // UpdateIndividualStat
  178. VOID
  179. UpdateVmStats(
  180. IN OUT PVM_STATS Stats,
  181. IN SIZE_T BlockSize
  182. )
  183. {
  184. UpdateIndividualStat( &Stats->Summary, BlockSize );
  185. if( IS_SMALL(BlockSize) ) {
  186. UpdateIndividualStat( &Stats->Small, BlockSize );
  187. }
  188. if( IS_MEDIUM(BlockSize) ) {
  189. UpdateIndividualStat( &Stats->Medium, BlockSize );
  190. }
  191. if( IS_LARGE(BlockSize) ) {
  192. UpdateIndividualStat( &Stats->Large, BlockSize );
  193. }
  194. } // UpdateVmStats
  195. VOID
  196. PrintVmStatsHeader(
  197. VOID
  198. )
  199. {
  200. dprintf(
  201. PRINTF_FORMAT,
  202. "TYPE",
  203. CCH_ULONG_COMMAS,
  204. "MINIMUM",
  205. CCH_ULONG_COMMAS,
  206. "MAXIMUM",
  207. CCH_ULONG_COMMAS,
  208. "AVERAGE",
  209. CCH_ULONG_COMMAS,
  210. "BLK COUNT"
  211. );
  212. printf(
  213. PRINTF_FORMAT,
  214. "~~~~",
  215. CCH_ULONG_COMMAS,
  216. "~~~~~~~",
  217. CCH_ULONG_COMMAS,
  218. "~~~~~~~",
  219. CCH_ULONG_COMMAS,
  220. "~~~~~~~",
  221. CCH_ULONG_COMMAS,
  222. "~~~~~~~~~"
  223. );
  224. } // PrintVmStatsHeader
  225. VOID
  226. PrintIndividualStat(
  227. IN PSTR Name,
  228. IN PINDIVIDUAL_STAT Stat
  229. )
  230. {
  231. SIZE_T average;
  232. SIZE_T minsize;
  233. CHAR minStr[CCH_ULONG_COMMAS];
  234. CHAR maxStr[CCH_ULONG_COMMAS];
  235. CHAR avgStr[CCH_ULONG_COMMAS];
  236. CHAR countStr[CCH_ULONG_COMMAS];
  237. if( Stat->BlockCount == 0 ) {
  238. average = 0;
  239. minsize = 0;
  240. } else {
  241. average = Stat->TotalSize / Stat->BlockCount;
  242. minsize = Stat->MinimumSize;
  243. }
  244. dprintf(
  245. PRINTF_FORMAT,
  246. Name,
  247. CCH_ULONG_COMMAS,
  248. ULongLongToString(
  249. (ULONGLONG)minsize,
  250. minStr
  251. ),
  252. CCH_ULONG_COMMAS,
  253. ULongLongToString(
  254. (ULONGLONG)Stat->MaximumSize,
  255. maxStr
  256. ),
  257. CCH_ULONG_COMMAS,
  258. ULongLongToString(
  259. (ULONGLONG)average,
  260. avgStr
  261. ),
  262. CCH_ULONG_COMMAS,
  263. ULongLongToString(
  264. (ULONGLONG)Stat->BlockCount,
  265. countStr
  266. )
  267. );
  268. } // PrintIndividualStat
  269. VOID
  270. PrintVmStats(
  271. IN PSTR Name,
  272. IN PVM_STATS Stats
  273. )
  274. {
  275. dprintf( "%s:\n", Name );
  276. PrintIndividualStat( "Small", &Stats->Small );
  277. PrintIndividualStat( "Medium", &Stats->Medium );
  278. PrintIndividualStat( "Large", &Stats->Large );
  279. PrintIndividualStat( "Summary", &Stats->Summary );
  280. dprintf( "\n" );
  281. } // PrintVmStats
  282. PSTR
  283. VmProtectToString(
  284. IN DWORD Protect,
  285. OUT PSTR Buffer
  286. )
  287. {
  288. INT i;
  289. PPROTECT_MASK mask;
  290. Buffer[0] = '\0';
  291. for( i = 0, mask = &ProtectMasks[0] ;
  292. (i < NUM_PROTECT_MASKS) && (Protect != 0) ;
  293. i++, mask++ ) {
  294. if( mask->Bit & Protect ) {
  295. Protect &= ~mask->Bit;
  296. if( Buffer[0] != '\0' ) {
  297. strcat( Buffer, "|" );
  298. }
  299. strcat( Buffer, mask->Name );
  300. }
  301. }
  302. if( Protect != 0 ) {
  303. if( Buffer[0] != '\0' ) {
  304. strcat( Buffer, "|" );
  305. }
  306. sprintf( Buffer + strlen(Buffer), "%08lx", Protect );
  307. }
  308. return Buffer;
  309. } // VmProtectToString
  310. PSTR
  311. VmStateToString(
  312. IN DWORD State,
  313. OUT PSTR Buffer
  314. )
  315. {
  316. PSTR result;
  317. CHAR invalidStr[sizeof("12345678")];
  318. switch( State )
  319. {
  320. case MEM_COMMIT:
  321. result = "Commit";
  322. break;
  323. case MEM_RESERVE:
  324. result = "Reserve";
  325. break;
  326. case MEM_FREE:
  327. result = "Free";
  328. break;
  329. default:
  330. sprintf( invalidStr, "%08lx", State );
  331. result = invalidStr;
  332. break;
  333. }
  334. strcpy( Buffer, result );
  335. return Buffer;
  336. } // VmStateToString
  337. PSTR
  338. VmTypeToString(
  339. IN DWORD Type,
  340. OUT PSTR Buffer
  341. )
  342. {
  343. PSTR result;
  344. CHAR invalidStr[sizeof("12345678")];
  345. switch( Type )
  346. {
  347. case MEM_PRIVATE:
  348. result = "Private";
  349. break;
  350. case MEM_MAPPED:
  351. result = "Mapped";
  352. break;
  353. case MEM_IMAGE:
  354. result = "Image";
  355. break;
  356. case 0:
  357. result = "";
  358. break;
  359. default:
  360. sprintf( invalidStr, "%08lx", Type );
  361. result = invalidStr;
  362. break;
  363. }
  364. strcpy( Buffer, result );
  365. return Buffer;
  366. } // VmTypeToString
  367. /************************************************************
  368. * Dump Virtual Memory Info
  369. ************************************************************/
  370. DECLARE_API( vmstat )
  371. /*++
  372. Routine Description:
  373. This function is called as an NTSD extension to format and dump
  374. virtual memory statistics.
  375. Arguments:
  376. hCurrentProcess - Supplies a handle to the current process (at the
  377. time the extension was called).
  378. hCurrentThread - Supplies a handle to the current thread (at the
  379. time the extension was called).
  380. CurrentPc - Supplies the current pc at the time the extension is
  381. called.
  382. lpExtensionApis - Supplies the address of the functions callable
  383. by this extension.
  384. lpArgumentString - Supplies the asciiz string that describes the
  385. ansi string to be dumped.
  386. Return Value:
  387. None.
  388. --*/
  389. {
  390. NTSTATUS status;
  391. ULONG_PTR address;
  392. MEMORY_BASIC_INFORMATION memInfo;
  393. VM_STATS freeStats;
  394. VM_STATS reserveStats;
  395. VM_STATS commitStats;
  396. VM_STATS privateStats;
  397. VM_STATS mappedStats;
  398. VM_STATS imageStats;
  399. INIT_API();
  400. //
  401. // Setup.
  402. //
  403. InitVmStats( &freeStats );
  404. InitVmStats( &reserveStats );
  405. InitVmStats( &commitStats );
  406. InitVmStats( &privateStats );
  407. InitVmStats( &mappedStats );
  408. InitVmStats( &imageStats );
  409. address = 0;
  410. //
  411. // Scan the virtual address space.
  412. //
  413. for( ; ; ) {
  414. status = NtQueryVirtualMemory(
  415. hCurrentProcess,
  416. (PVOID)address,
  417. MemoryBasicInformation,
  418. &memInfo,
  419. sizeof(memInfo),
  420. NULL
  421. );
  422. if( !NT_SUCCESS(status) ) {
  423. break;
  424. }
  425. //
  426. // Interpret the memory state.
  427. //
  428. switch( memInfo.State ) {
  429. case MEM_FREE:
  430. UpdateVmStats( &freeStats, memInfo.RegionSize );
  431. break;
  432. case MEM_RESERVE:
  433. UpdateVmStats( &reserveStats, memInfo.RegionSize );
  434. break;
  435. case MEM_COMMIT:
  436. UpdateVmStats( &commitStats, memInfo.RegionSize );
  437. break;
  438. }
  439. //
  440. // Interpret the memory type.
  441. //
  442. switch( memInfo.Type ) {
  443. case MEM_PRIVATE:
  444. UpdateVmStats( &privateStats, memInfo.RegionSize );
  445. break;
  446. case MEM_MAPPED:
  447. UpdateVmStats( &mappedStats, memInfo.RegionSize );
  448. break;
  449. case MEM_IMAGE:
  450. UpdateVmStats( &imageStats, memInfo.RegionSize );
  451. break;
  452. }
  453. //
  454. // Advance to the next block.
  455. //
  456. address += memInfo.RegionSize;
  457. }
  458. //
  459. // Dump it.
  460. //
  461. PrintVmStatsHeader();
  462. PrintVmStats( "Free", &freeStats );
  463. PrintVmStats( "Reserve", &reserveStats );
  464. PrintVmStats( "Commit", &commitStats );
  465. PrintVmStats( "Private", &privateStats );
  466. PrintVmStats( "Mapped", &mappedStats );
  467. PrintVmStats( "Image", &imageStats );
  468. } // DECLARE_API( vmstat )
  469. DECLARE_API( vmmap )
  470. /*++
  471. Routine Description:
  472. This function is called as an NTSD extension to format and dump
  473. the debugee's virtual memory address space.
  474. Arguments:
  475. hCurrentProcess - Supplies a handle to the current process (at the
  476. time the extension was called).
  477. hCurrentThread - Supplies a handle to the current thread (at the
  478. time the extension was called).
  479. CurrentPc - Supplies the current pc at the time the extension is
  480. called.
  481. lpExtensionApis - Supplies the address of the functions callable
  482. by this extension.
  483. lpArgumentString - Supplies the asciiz string that describes the
  484. ansi string to be dumped.
  485. Return Value:
  486. None.
  487. --*/
  488. {
  489. NTSTATUS status;
  490. ULONG_PTR address;
  491. MEMORY_BASIC_INFORMATION memInfo;
  492. CHAR protectStr[32];
  493. CHAR aprotectStr[32];
  494. CHAR stateStr[16];
  495. CHAR typeStr[16];
  496. INIT_API();
  497. //
  498. // Setup.
  499. //
  500. address = 0;
  501. dprintf(
  502. "%-*s %-*s %-*s %-13s %-13s %-8s %-8s\n",
  503. sizeof(PVOID) * 2,
  504. "Start",
  505. sizeof(PVOID) * 2,
  506. "Stop",
  507. sizeof(PVOID) * 2,
  508. "Length",
  509. "AllocProtect",
  510. "Protect",
  511. "State",
  512. "Type"
  513. );
  514. //
  515. // Scan the virtual address space.
  516. //
  517. for( ; ; ) {
  518. status = NtQueryVirtualMemory(
  519. hCurrentProcess,
  520. (PVOID)address,
  521. MemoryBasicInformation,
  522. &memInfo,
  523. sizeof(memInfo),
  524. NULL
  525. );
  526. if( !NT_SUCCESS(status) ) {
  527. break;
  528. }
  529. //
  530. // Dump the current entry.
  531. //
  532. dprintf(
  533. "%p-%p %p %-13s %-13s %-8s %-8s\n",
  534. memInfo.BaseAddress,
  535. (ULONG_PTR)memInfo.BaseAddress + memInfo.RegionSize - 1,
  536. memInfo.RegionSize,
  537. VmProtectToString( memInfo.AllocationProtect, aprotectStr ),
  538. VmProtectToString( memInfo.Protect, protectStr ),
  539. VmStateToString( memInfo.State, stateStr ),
  540. VmTypeToString( memInfo.Type, typeStr )
  541. );
  542. //
  543. // Advance to the next block.
  544. //
  545. address += memInfo.RegionSize;
  546. }
  547. } // DECLARE_API( vmmap )