Leaked source code of windows server 2003
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.

550 lines
13 KiB

  1. // poolsnap.c
  2. // this program takes a snapshot of all the kernel pool tags.
  3. // and appends it to the logfile (arg)
  4. // pmon was model for this
  5. /* includes */
  6. #include <nt.h>
  7. #include <ntrtl.h>
  8. #include <nturtl.h>
  9. #include <windows.h>
  10. #include <assert.h>
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <string.h>
  14. #include <ctype.h>
  15. #include <io.h>
  16. #include <srvfsctl.h>
  17. #include <search.h>
  18. #if !defined(POOLSNAP_INCLUDED)
  19. #define SORTLOG_INCLUDED
  20. #define ANALOG_INCLUDED
  21. #include "analog.c"
  22. #include "sortlog.c"
  23. #endif
  24. #include "tags.c"
  25. //
  26. // declarations
  27. //
  28. int __cdecl ulcomp(const void *e1,const void *e2);
  29. NTSTATUS
  30. QueryPoolTagInformationIterative(
  31. PUCHAR *CurrentBuffer,
  32. size_t *CurrentBufferSize
  33. );
  34. //
  35. // definitions
  36. //
  37. #define NONPAGED 0
  38. #define PAGED 1
  39. #define BOTH 2
  40. //
  41. // Printf format string for pool tag info.
  42. //
  43. #ifdef _WIN64
  44. #define POOLTAG_PRINT_FORMAT " %4s %5s %18I64d %18I64d %16I64d %14I64d %12I64d\n"
  45. #else
  46. #define POOLTAG_PRINT_FORMAT " %4s %5s %9ld %9ld %8ld %7ld %6ld\n"
  47. #endif
  48. // from poolmon
  49. // raw input
  50. PSYSTEM_POOLTAG_INFORMATION PoolInfo;
  51. //
  52. // the amount of memory to increase the size
  53. // of the buffer for NtQuerySystemInformation at each step
  54. //
  55. #define BUFFER_SIZE_STEP 65536
  56. //
  57. // the buffer used for NtQuerySystemInformation
  58. //
  59. PUCHAR CurrentBuffer = NULL;
  60. //
  61. // the size of the buffer used for NtQuerySystemInformation
  62. //
  63. size_t CurrentBufferSize = 0;
  64. //
  65. // formatted output
  66. //
  67. typedef struct _POOLMON_OUT {
  68. union {
  69. UCHAR Tag[4];
  70. ULONG TagUlong;
  71. };
  72. UCHAR NullByte;
  73. BOOL Changed;
  74. ULONG Type;
  75. SIZE_T Allocs[2];
  76. SIZE_T AllocsDiff[2];
  77. SIZE_T Frees[2];
  78. SIZE_T FreesDiff[2];
  79. SIZE_T Allocs_Frees[2];
  80. SIZE_T Used[2];
  81. SIZE_T UsedDiff[2];
  82. SIZE_T Each[2];
  83. } POOLMON_OUT, *PPOOLMON_OUT;
  84. PPOOLMON_OUT OutBuffer;
  85. PPOOLMON_OUT Out;
  86. UCHAR *PoolType[] = {
  87. "Nonp ",
  88. "Paged"};
  89. VOID PoolsnapUsage(VOID)
  90. {
  91. printf("poolsnap [-?] [-t] [<logfile>]\n");
  92. printf("poolsnap logs system pool usage to <logfile>\n");
  93. printf("<logfile> = poolsnap.log by default\n");
  94. printf("-? Gives this help\n");
  95. printf("-a Analyze the log file for leaks.\n");
  96. printf("-t Output extra tagged information\n");
  97. exit(-1);
  98. }
  99. #if !defined(POOLSNAP_INCLUDED)
  100. VOID
  101. AnalyzeLog (
  102. PCHAR FileName,
  103. BOOL HtmlOutput
  104. )
  105. {
  106. char * Args[4];
  107. UNREFERENCED_PARAMETER(HtmlOutput);
  108. Args[0] = "memsnap.exe";
  109. Args[1] = FileName;
  110. Args[2] = "_memsnap_temp_";
  111. Args[3] = NULL;
  112. SortlogMain (3, Args);
  113. Args[0] = "memsnap.exe";
  114. Args[1] = "-d";
  115. Args[2] = "_memsnap_temp_";
  116. Args[3] = NULL;
  117. AnalogMain (3, Args);
  118. DeleteFile ("_memsnap_temp_");
  119. }
  120. #endif
  121. /*
  122. * FUNCTION: Main
  123. *
  124. * ARGUMENTS: See Usage
  125. *
  126. * RETURNS: 0
  127. *
  128. */
  129. #if defined(POOLSNAP_INCLUDED)
  130. int __cdecl PoolsnapMain (int argc, char* argv[])
  131. #else
  132. int __cdecl main (int argc, char* argv[])
  133. #endif
  134. {
  135. NTSTATUS Status; // status from NT api
  136. FILE* LogFile= NULL; // log file handle
  137. DWORD x= 0; // counter
  138. SIZE_T NumberOfPoolTags;
  139. INT iCmdIndex; // index into argv
  140. BOOL bOutputTags= FALSE; // if true, output standard tags
  141. // get higher priority in case system is bogged down
  142. if ( GetPriorityClass(GetCurrentProcess()) == NORMAL_PRIORITY_CLASS) {
  143. SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS);
  144. }
  145. //
  146. // parse command line arguments
  147. //
  148. for( iCmdIndex=1; iCmdIndex < argc; iCmdIndex++ ) {
  149. CHAR chr;
  150. chr= *argv[iCmdIndex];
  151. if( (chr=='-') || (chr=='/') ) {
  152. chr= argv[iCmdIndex][1];
  153. switch( chr ) {
  154. case '?':
  155. PoolsnapUsage();
  156. break;
  157. case 't': case 'T':
  158. bOutputTags= TRUE;
  159. break;
  160. case 'a':
  161. case 'A':
  162. if (argv[iCmdIndex + 1] != NULL) {
  163. AnalyzeLog (argv[iCmdIndex + 1], FALSE);
  164. }
  165. else {
  166. AnalyzeLog ("poolsnap.log", FALSE);
  167. }
  168. exit (0);
  169. default:
  170. printf("Invalid switch: %s\n",argv[iCmdIndex]);
  171. PoolsnapUsage();
  172. break;
  173. }
  174. }
  175. else {
  176. if( LogFile ) {
  177. printf("Error: more than one file specified: %s\n",argv[iCmdIndex]);
  178. return(0);
  179. }
  180. LogFile= fopen(argv[iCmdIndex],"a");
  181. if( !LogFile ) {
  182. printf("Error: Opening file %s\n",argv[iCmdIndex]);
  183. return(0);
  184. }
  185. }
  186. }
  187. //
  188. // if no file specified, use default name
  189. //
  190. if( !LogFile ) {
  191. if( (LogFile = fopen("poolsnap.log","a")) == NULL ) {
  192. printf("Error: opening file poolsnap.log\n");
  193. return(0);
  194. }
  195. }
  196. //
  197. // print file header once
  198. //
  199. if( _filelength(_fileno(LogFile)) == 0 ) {
  200. fprintf(LogFile," Tag Type Allocs Frees Diff Bytes Per Alloc\n");
  201. }
  202. fprintf(LogFile,"\n");
  203. if( bOutputTags ) {
  204. OutputStdTags(LogFile, "poolsnap" );
  205. }
  206. // grab all pool information
  207. // log line format, fixed column format
  208. Status = QueryPoolTagInformationIterative(
  209. &CurrentBuffer,
  210. &CurrentBufferSize
  211. );
  212. if (! NT_SUCCESS(Status)) {
  213. printf("Failed to query pool tags information (status %08X). \n", Status);
  214. printf("Please check if pool tags are enabled. \n");
  215. return (0);
  216. }
  217. PoolInfo = (PSYSTEM_POOLTAG_INFORMATION)CurrentBuffer;
  218. //
  219. // Allocate the output buffer.
  220. //
  221. OutBuffer = malloc (PoolInfo->Count * sizeof(POOLMON_OUT));
  222. if (OutBuffer == NULL) {
  223. printf ("Error: cannot allocate internal buffer of %p bytes \n",
  224. (PVOID)(PoolInfo->Count * sizeof(POOLMON_OUT)));
  225. return (0);
  226. }
  227. Out = OutBuffer;
  228. if( NT_SUCCESS(Status) ) {
  229. for (x = 0; x < (int)PoolInfo->Count; x++) {
  230. // get pool info from buffer
  231. Out->Type = 0;
  232. // non-paged
  233. if (PoolInfo->TagInfo[x].NonPagedAllocs != 0) {
  234. Out->Allocs[NONPAGED] = PoolInfo->TagInfo[x].NonPagedAllocs;
  235. Out->Frees[NONPAGED] = PoolInfo->TagInfo[x].NonPagedFrees;
  236. Out->Used[NONPAGED] = PoolInfo->TagInfo[x].NonPagedUsed;
  237. Out->Allocs_Frees[NONPAGED] = PoolInfo->TagInfo[x].NonPagedAllocs -
  238. PoolInfo->TagInfo[x].NonPagedFrees;
  239. Out->TagUlong = PoolInfo->TagInfo[x].TagUlong;
  240. Out->Type |= (1 << NONPAGED);
  241. Out->Changed = FALSE;
  242. Out->NullByte = '\0';
  243. Out->Each[NONPAGED] = Out->Used[NONPAGED] /
  244. (Out->Allocs_Frees[NONPAGED]?Out->Allocs_Frees[NONPAGED]:1);
  245. }
  246. // paged
  247. if (PoolInfo->TagInfo[x].PagedAllocs != 0) {
  248. Out->Allocs[PAGED] = PoolInfo->TagInfo[x].PagedAllocs;
  249. Out->Frees[PAGED] = PoolInfo->TagInfo[x].PagedFrees;
  250. Out->Used[PAGED] = PoolInfo->TagInfo[x].PagedUsed;
  251. Out->Allocs_Frees[PAGED] = PoolInfo->TagInfo[x].PagedAllocs -
  252. PoolInfo->TagInfo[x].PagedFrees;
  253. Out->TagUlong = PoolInfo->TagInfo[x].TagUlong;
  254. Out->Type |= (1 << PAGED);
  255. Out->Changed = FALSE;
  256. Out->NullByte = '\0';
  257. Out->Each[PAGED] = Out->Used[PAGED] /
  258. (Out->Allocs_Frees[PAGED]?Out->Allocs_Frees[PAGED]:1);
  259. }
  260. Out += 1;
  261. }
  262. }
  263. else {
  264. fprintf(LogFile, "Query pooltags Failed %lx\n",Status);
  265. fprintf(LogFile, " Be sure to turn on 'enable pool tagging' in gflags and reboot.\n");
  266. if( bOutputTags ) {
  267. fprintf(LogFile, "!Error:Query pooltags failed %lx\n",Status);
  268. fprintf(LogFile, "!Error: Be sure to turn on 'enable pool tagging' in gflags and reboot.\n");
  269. }
  270. // If there is an operator around, wake him up, but keep moving
  271. Beep(1000,350); Beep(500,350); Beep(1000,350);
  272. exit(0);
  273. }
  274. //
  275. // sort by tag value which is big endian
  276. //
  277. NumberOfPoolTags = Out - OutBuffer;
  278. qsort((void *)OutBuffer,
  279. (size_t)NumberOfPoolTags,
  280. (size_t)sizeof(POOLMON_OUT),
  281. ulcomp);
  282. //
  283. // print in file
  284. //
  285. for (x = 0; x < (int)PoolInfo->Count; x++) {
  286. if ((OutBuffer[x].Type & (1 << NONPAGED))) {
  287. fprintf(LogFile,
  288. POOLTAG_PRINT_FORMAT,
  289. OutBuffer[x].Tag,
  290. PoolType[NONPAGED],
  291. OutBuffer[x].Allocs[NONPAGED],
  292. OutBuffer[x].Frees[NONPAGED],
  293. OutBuffer[x].Allocs_Frees[NONPAGED],
  294. OutBuffer[x].Used[NONPAGED],
  295. OutBuffer[x].Each[NONPAGED]);
  296. }
  297. if ((OutBuffer[x].Type & (1 << PAGED))) {
  298. fprintf(LogFile,
  299. POOLTAG_PRINT_FORMAT,
  300. OutBuffer[x].Tag,
  301. PoolType[PAGED],
  302. OutBuffer[x].Allocs[PAGED],
  303. OutBuffer[x].Frees[PAGED],
  304. OutBuffer[x].Allocs_Frees[PAGED],
  305. OutBuffer[x].Used[PAGED],
  306. OutBuffer[x].Each[PAGED]);
  307. }
  308. }
  309. // close file
  310. fclose(LogFile);
  311. return 0;
  312. }
  313. // comparison function for qsort
  314. // Tags are big endian
  315. int __cdecl ulcomp(const void *e1,const void *e2)
  316. {
  317. ULONG u1;
  318. u1 = ((PUCHAR)e1)[0] - ((PUCHAR)e2)[0];
  319. if (u1 != 0) {
  320. return u1;
  321. }
  322. u1 = ((PUCHAR)e1)[1] - ((PUCHAR)e2)[1];
  323. if (u1 != 0) {
  324. return u1;
  325. }
  326. u1 = ((PUCHAR)e1)[2] - ((PUCHAR)e2)[2];
  327. if (u1 != 0) {
  328. return u1;
  329. }
  330. u1 = ((PUCHAR)e1)[3] - ((PUCHAR)e2)[3];
  331. return u1;
  332. }
  333. /*
  334. * FUNCTION:
  335. *
  336. * QueryPoolTagInformationIterative
  337. *
  338. * ARGUMENTS:
  339. *
  340. * CurrentBuffer - a pointer to the buffer currently used for
  341. * NtQuerySystemInformation( SystemPoolTagInformation ).
  342. * It will be allocated if NULL or its size grown
  343. * if necessary.
  344. *
  345. * CurrentBufferSize - a pointer to a variable that holds the current
  346. * size of the buffer.
  347. *
  348. *
  349. * RETURNS:
  350. *
  351. * NTSTATUS returned by NtQuerySystemInformation or
  352. * STATUS_INSUFFICIENT_RESOURCES if the buffer must grow and the
  353. * heap allocation for it fails.
  354. *
  355. */
  356. NTSTATUS
  357. QueryPoolTagInformationIterative(
  358. PUCHAR *CurrentBuffer,
  359. size_t *CurrentBufferSize
  360. )
  361. {
  362. size_t NewBufferSize;
  363. NTSTATUS ReturnedStatus = STATUS_SUCCESS;
  364. if( CurrentBuffer == NULL || CurrentBufferSize == NULL ) {
  365. return STATUS_INVALID_PARAMETER;
  366. }
  367. if( *CurrentBufferSize == 0 || *CurrentBuffer == NULL ) {
  368. //
  369. // there is no buffer allocated yet
  370. //
  371. NewBufferSize = sizeof( UCHAR ) * BUFFER_SIZE_STEP;
  372. *CurrentBuffer = (PUCHAR) malloc( NewBufferSize );
  373. if( *CurrentBuffer != NULL ) {
  374. *CurrentBufferSize = NewBufferSize;
  375. } else {
  376. //
  377. // insufficient memory
  378. //
  379. ReturnedStatus = STATUS_INSUFFICIENT_RESOURCES;
  380. }
  381. }
  382. //
  383. // iterate by buffer's size
  384. //
  385. while( *CurrentBuffer != NULL ) {
  386. ReturnedStatus = NtQuerySystemInformation (
  387. SystemPoolTagInformation,
  388. *CurrentBuffer,
  389. (ULONG)*CurrentBufferSize,
  390. NULL );
  391. if( ! NT_SUCCESS(ReturnedStatus) ) {
  392. //
  393. // free the current buffer
  394. //
  395. free( *CurrentBuffer );
  396. *CurrentBuffer = NULL;
  397. if (ReturnedStatus == STATUS_INFO_LENGTH_MISMATCH) {
  398. //
  399. // try with a greater buffer size
  400. //
  401. NewBufferSize = *CurrentBufferSize + BUFFER_SIZE_STEP;
  402. *CurrentBuffer = (PUCHAR) malloc( NewBufferSize );
  403. if( *CurrentBuffer != NULL ) {
  404. //
  405. // allocated new buffer
  406. //
  407. *CurrentBufferSize = NewBufferSize;
  408. } else {
  409. //
  410. // insufficient memory
  411. //
  412. ReturnedStatus = STATUS_INSUFFICIENT_RESOURCES;
  413. *CurrentBufferSize = 0;
  414. }
  415. } else {
  416. *CurrentBufferSize = 0;
  417. }
  418. } else {
  419. //
  420. // NtQuerySystemInformation returned success
  421. //
  422. break;
  423. }
  424. }
  425. return ReturnedStatus;
  426. }