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.

807 lines
19 KiB

  1. // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
  2. //
  3. // Copyright (c) 1985-2000 Microsoft Corporation
  4. //
  5. // This file is part of the Microsoft Research IPv6 Network Protocol Stack.
  6. // You should have received a copy of the Microsoft End-User License Agreement
  7. // for this software along with this release; see the file "license.txt".
  8. // If not, please see http://www.research.microsoft.com/msripv6/license.htm,
  9. // or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
  10. //
  11. // Abstract:
  12. //
  13. // Debugging code.
  14. //
  15. #include <ntosp.h>
  16. #undef ExAllocatePoolWithTag
  17. #undef ExFreePool
  18. //
  19. // This is copied from ntos\inc\ex.h
  20. //
  21. #if !defined(POOL_TAGGING)
  22. #define ExAllocatePoolWithTag(a,b,c) ExAllocatePool(a,b)
  23. #endif // !POOL_TAGGING
  24. #ifndef COUNTING_MALLOC
  25. #define COUNTING_MALLOC DBG
  26. #endif
  27. #if COUNTING_MALLOC
  28. //
  29. // This enumerated type is used as the function return
  30. // value of the function that is used to search the tree
  31. // for a key. SisFoundNode indicates that the function found
  32. // the key. SisInsertAsLeft indicates that the key was not found
  33. // and the node should be inserted as the left child of the
  34. // parent. SisInsertAsRight indicates that the key was not found
  35. // and the node should be inserted as the right child of the
  36. // parent.
  37. //
  38. typedef enum _SIS_SEARCH_RESULT{
  39. SisEmptyTree,
  40. SisFoundNode,
  41. SisInsertAsLeft,
  42. SisInsertAsRight
  43. } SIS_SEARCH_RESULT;
  44. typedef
  45. LONG
  46. (NTAPI *PSIS_TREE_COMPARE_ROUTINE) (
  47. PVOID Key,
  48. PVOID Node
  49. );
  50. typedef struct _SIS_TREE {
  51. PRTL_SPLAY_LINKS TreeRoot;
  52. PSIS_TREE_COMPARE_ROUTINE CompareRoutine;
  53. } SIS_TREE, *PSIS_TREE;
  54. static
  55. SIS_SEARCH_RESULT
  56. FindNodeOrParent(
  57. IN PSIS_TREE Tree,
  58. IN PVOID Key,
  59. OUT PRTL_SPLAY_LINKS *NodeOrParent
  60. )
  61. /*++
  62. Routine Description:
  63. This routine is private to the tree package and will
  64. find and return (via the NodeOrParent parameter) the node
  65. with the given key, or if that node is not in the tree it
  66. will return (via the NodeOrParent parameter) a pointer to
  67. the parent.
  68. Arguments:
  69. Tree - The tree to search for the key.
  70. Key - Pointer to a buffer holding the key. The tree
  71. package doesn't examine the key itself. It leaves
  72. this up to the user supplied compare routine.
  73. NodeOrParent - Will be set to point to the node containing the
  74. the key or what should be the parent of the node
  75. if it were in the tree. Note that this will *NOT*
  76. be set if the search result is SisEmptyTree.
  77. Return Value:
  78. SIS_SEARCH_RESULT - SisEmptyTree: The tree was empty. NodeOrParent
  79. is *not* altered.
  80. SisFoundNode: A node with the key is in the tree.
  81. NodeOrParent points to that node.
  82. SisInsertAsLeft: Node with key was not found.
  83. NodeOrParent points to what would be
  84. parent. The node would be the left
  85. child.
  86. SisInsertAsRight: Node with key was not found.
  87. NodeOrParent points to what would be
  88. parent. The node would be the right
  89. child.
  90. --*/
  91. {
  92. if (Tree->TreeRoot == NULL) {
  93. return SisEmptyTree;
  94. } else {
  95. //
  96. // Used as the iteration variable while stepping through
  97. // the tree.
  98. //
  99. PRTL_SPLAY_LINKS NodeToExamine = Tree->TreeRoot;
  100. //
  101. // Just a temporary. Hopefully a good compiler will get
  102. // rid of it.
  103. //
  104. PRTL_SPLAY_LINKS Child;
  105. //
  106. // Holds the value of the comparasion.
  107. //
  108. int Result;
  109. while (TRUE) {
  110. //
  111. // Compare the buffer with the key in the tree element.
  112. //
  113. Result = Tree->CompareRoutine(
  114. Key,
  115. NodeToExamine
  116. );
  117. if (Result < 0) {
  118. if (Child = RtlLeftChild(NodeToExamine)) {
  119. NodeToExamine = Child;
  120. } else {
  121. //
  122. // Node is not in the tree. Set the output
  123. // parameter to point to what would be its
  124. // parent and return which child it would be.
  125. //
  126. *NodeOrParent = NodeToExamine;
  127. return SisInsertAsLeft;
  128. }
  129. } else if (Result > 0) {
  130. if (Child = RtlRightChild(NodeToExamine)) {
  131. NodeToExamine = Child;
  132. } else {
  133. //
  134. // Node is not in the tree. Set the output
  135. // parameter to point to what would be its
  136. // parent and return which child it would be.
  137. //
  138. *NodeOrParent = NodeToExamine;
  139. return SisInsertAsRight;
  140. }
  141. } else {
  142. //
  143. // Node is in the tree (or it better be because of the
  144. // assert). Set the output parameter to point to
  145. // the node and tell the caller that we found the node.
  146. //
  147. ASSERT(Result == 0);
  148. *NodeOrParent = NodeToExamine;
  149. return SisFoundNode;
  150. }
  151. }
  152. }
  153. }
  154. VOID
  155. SipInitializeTree (
  156. IN PSIS_TREE Tree,
  157. IN PSIS_TREE_COMPARE_ROUTINE CompareRoutine
  158. )
  159. /*++
  160. Routine Description:
  161. The procedure InitializeTree prepares a tree for use.
  162. This must be called for every individual tree variable before
  163. it can be used.
  164. Arguments:
  165. Tree - Pointer to the tree to be initialized.
  166. CompareRoutine - User routine to be used to compare to keys in the
  167. tree.
  168. Return Value:
  169. None.
  170. --*/
  171. {
  172. Tree->TreeRoot = NULL;
  173. Tree->CompareRoutine = CompareRoutine;
  174. }
  175. PVOID
  176. SipInsertElementTree (
  177. IN PSIS_TREE Tree,
  178. IN PVOID Node,
  179. IN PVOID Key
  180. )
  181. /*++
  182. Routine Description:
  183. The function SipInsertElementTree will insert a new element in a tree.
  184. If an element with the same key already exists in the tree the return
  185. value is a pointer to the old element. Otherwise, the return value is
  186. a pointer to the new element. Note that this differs from the Rtl
  187. generic table package in that the actual node passed in is inserted in
  188. the tree, whereas the table package inserts a copy of the node.
  189. Arguments:
  190. Tree - Pointer to the tree in which to (possibly) insert the
  191. node.
  192. Node - Pointer to the node to insert in the tree. Will not be inserted
  193. if a node with a matching key is found.
  194. Key - Passed to the user comparasion routine.
  195. Return Value:
  196. PVOID - Pointer to the new node or the existing node if one exists.
  197. --*/
  198. {
  199. //
  200. // Holds a pointer to the node in the tree or what would be the
  201. // parent of the node.
  202. //
  203. PRTL_SPLAY_LINKS NodeOrParent;
  204. //
  205. // Holds the result of the tree lookup.
  206. //
  207. SIS_SEARCH_RESULT Lookup;
  208. //
  209. // Node will point to the splay links of what
  210. // will be returned to the user.
  211. //
  212. PRTL_SPLAY_LINKS NodeToReturn = (PRTL_SPLAY_LINKS) Node;
  213. Lookup = FindNodeOrParent(
  214. Tree,
  215. Key,
  216. &NodeOrParent
  217. );
  218. if (Lookup != SisFoundNode) {
  219. RtlInitializeSplayLinks(NodeToReturn);
  220. //
  221. // Insert the new node in the tree.
  222. //
  223. if (Lookup == SisEmptyTree) {
  224. Tree->TreeRoot = NodeToReturn;
  225. } else {
  226. if (Lookup == SisInsertAsLeft) {
  227. RtlInsertAsLeftChild(
  228. NodeOrParent,
  229. NodeToReturn
  230. );
  231. } else {
  232. RtlInsertAsRightChild(
  233. NodeOrParent,
  234. NodeToReturn
  235. );
  236. }
  237. }
  238. } else {
  239. NodeToReturn = NodeOrParent;
  240. }
  241. //
  242. // Always splay the (possibly) new node.
  243. //
  244. Tree->TreeRoot = RtlSplay(NodeToReturn);
  245. return NodeToReturn;
  246. }
  247. VOID
  248. SipDeleteElementTree (
  249. IN PSIS_TREE Tree,
  250. IN PVOID Node
  251. )
  252. /*++
  253. Routine Description:
  254. The function SipDeleteElementTree will remove an element
  255. from a tree. Note that the memory associated with the node
  256. is not actually freed.
  257. Arguments:
  258. Tree - Pointer to the tree in which to remove the specified node.
  259. Node - Node of tree to remove.
  260. Return Value:
  261. None.
  262. --*/
  263. {
  264. PRTL_SPLAY_LINKS NodeToDelete = (PRTL_SPLAY_LINKS) Node;
  265. //
  266. // Delete the node from the tree. Note that RtlDelete
  267. // will splay the tree.
  268. //
  269. Tree->TreeRoot = RtlDelete(NodeToDelete);
  270. }
  271. PVOID
  272. SipLookupElementTree (
  273. IN PSIS_TREE Tree,
  274. IN PVOID Key
  275. )
  276. /*++
  277. Routine Description:
  278. The function SipLookupElementTree will find an element in a
  279. tree. If the element is located the return value is a pointer to
  280. the element, otherwise if the element is not located the return
  281. value is NULL.
  282. Arguments:
  283. Tree - Pointer to the users tree to search for the key.
  284. Key - Used for the comparasion.
  285. Return Value:
  286. PVOID - returns a pointer to the user data.
  287. --*/
  288. {
  289. //
  290. // Holds a pointer to the node in the tree or what would be the
  291. // parent of the node.
  292. //
  293. PRTL_SPLAY_LINKS NodeOrParent;
  294. //
  295. // Holds the result of the tree lookup.
  296. //
  297. SIS_SEARCH_RESULT Lookup;
  298. Lookup = FindNodeOrParent(
  299. Tree,
  300. Key,
  301. &NodeOrParent
  302. );
  303. if (Lookup == SisEmptyTree) {
  304. return NULL;
  305. } else {
  306. //
  307. // Splay the tree with this node. Note that we do this irregardless
  308. // of whether the node was found.
  309. //
  310. Tree->TreeRoot = RtlSplay(NodeOrParent);
  311. //
  312. // Return a pointer to the user data.
  313. //
  314. if (Lookup == SisFoundNode) {
  315. return NodeOrParent;
  316. } else {
  317. return NULL;
  318. }
  319. }
  320. }
  321. VOID
  322. SipDeleteTree (
  323. IN PSIS_TREE Tree
  324. )
  325. /*++
  326. Routine Description:
  327. Deletes and frees all elements in a tree.
  328. Does not free the tree structure itself.
  329. Arguments:
  330. Tree - Pointer to the tree to be deleted.
  331. Return Value:
  332. None.
  333. --*/
  334. {
  335. PVOID Node;
  336. while ((Node = Tree->TreeRoot) != NULL) {
  337. SipDeleteElementTree(Tree, Node);
  338. ExFreePool(Node);
  339. }
  340. }
  341. typedef struct _SIS_COUNTING_MALLOC_CLASS_KEY {
  342. POOL_TYPE poolType;
  343. ULONG tag;
  344. PCHAR file;
  345. ULONG line;
  346. } SIS_COUNTING_MALLOC_CLASS_KEY, *PSIS_COUNTING_MALLOC_CLASS_KEY;
  347. typedef struct _SIS_COUNTING_MALLOC_CLASS_ENTRY {
  348. RTL_SPLAY_LINKS;
  349. SIS_COUNTING_MALLOC_CLASS_KEY;
  350. ULONG numberOutstanding;
  351. ULONG bytesOutstanding;
  352. ULONG numberEverAllocated;
  353. LONGLONG bytesEverAllocated;
  354. struct _SIS_COUNTING_MALLOC_CLASS_ENTRY *prev, *next;
  355. } SIS_COUNTING_MALLOC_CLASS_ENTRY, *PSIS_COUNTING_MALLOC_CLASS_ENTRY;
  356. typedef struct _SIS_COUNTING_MALLOC_KEY {
  357. PVOID p;
  358. } SIS_COUNTING_MALLOC_KEY, *PSIS_COUNTING_MALLOC_KEY;
  359. typedef struct _SIS_COUNTING_MALLOC_ENTRY {
  360. RTL_SPLAY_LINKS;
  361. SIS_COUNTING_MALLOC_KEY;
  362. PSIS_COUNTING_MALLOC_CLASS_ENTRY classEntry;
  363. ULONG byteCount;
  364. } SIS_COUNTING_MALLOC_ENTRY, *PSIS_COUNTING_MALLOC_ENTRY;
  365. KSPIN_LOCK CountingMallocLock[1];
  366. BOOLEAN CountingMallocInternalFailure = FALSE;
  367. SIS_COUNTING_MALLOC_CLASS_ENTRY CountingMallocClassListHead[1];
  368. SIS_TREE CountingMallocClassTree[1];
  369. SIS_TREE CountingMallocTree[1];
  370. LONG NTAPI
  371. CountingMallocClassCompareRoutine(
  372. PVOID Key,
  373. PVOID Node)
  374. {
  375. PSIS_COUNTING_MALLOC_CLASS_KEY key = Key;
  376. PSIS_COUNTING_MALLOC_CLASS_ENTRY entry = Node;
  377. if (key->poolType > entry->poolType) return 1;
  378. if (key->poolType < entry->poolType) return -1;
  379. ASSERT(key->poolType == entry->poolType);
  380. if (key->tag > entry->tag) return 1;
  381. if (key->tag < entry->tag) return -1;
  382. ASSERT(key->tag == entry->tag);
  383. if (key->file > entry->file) return 1;
  384. if (key->file < entry->file) return -1;
  385. ASSERT(key->file == entry->file);
  386. if (key->line > entry->line) return 1;
  387. if (key->line < entry->line) return -1;
  388. ASSERT(key->line == entry->line);
  389. return 0;
  390. }
  391. LONG NTAPI
  392. CountingMallocCompareRoutine(
  393. PVOID Key,
  394. PVOID Node)
  395. {
  396. PSIS_COUNTING_MALLOC_KEY key = Key;
  397. PSIS_COUNTING_MALLOC_ENTRY entry = Node;
  398. if (key->p < entry->p) return 1;
  399. if (key->p > entry->p) return -1;
  400. ASSERT(key->p == entry->p);
  401. return 0;
  402. }
  403. VOID *
  404. CountingExAllocatePoolWithTag(
  405. IN POOL_TYPE PoolType,
  406. IN ULONG NumberOfBytes,
  407. IN ULONG Tag,
  408. IN PCHAR File,
  409. IN ULONG Line)
  410. {
  411. PVOID memoryFromExAllocate;
  412. KIRQL OldIrql;
  413. SIS_COUNTING_MALLOC_CLASS_KEY classKey[1];
  414. PSIS_COUNTING_MALLOC_CLASS_ENTRY classEntry;
  415. SIS_COUNTING_MALLOC_KEY key[1];
  416. PSIS_COUNTING_MALLOC_ENTRY entry;
  417. //
  418. // First do the actual malloc
  419. //
  420. memoryFromExAllocate = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag);
  421. if (NULL == memoryFromExAllocate) {
  422. //
  423. // We're out of memory. Punt.
  424. //
  425. return NULL;
  426. }
  427. KeAcquireSpinLock(CountingMallocLock, &OldIrql);
  428. //
  429. // See if we already have a class entry for this tag/poolType pair.
  430. //
  431. classKey->tag = Tag;
  432. classKey->poolType = PoolType;
  433. classKey->file = File;
  434. classKey->line = Line;
  435. classEntry = SipLookupElementTree(CountingMallocClassTree, classKey);
  436. if (NULL == classEntry) {
  437. //
  438. // This is the first time we've seen a malloc of this class.
  439. //
  440. classEntry = ExAllocatePoolWithTag(NonPagedPool, sizeof(SIS_COUNTING_MALLOC_CLASS_ENTRY), ' siS');
  441. if (NULL == classEntry) {
  442. CountingMallocInternalFailure = TRUE;
  443. KeReleaseSpinLock(CountingMallocLock, OldIrql);
  444. return memoryFromExAllocate;
  445. }
  446. //
  447. // Fill in the new class entry.
  448. //
  449. classEntry->tag = Tag;
  450. classEntry->poolType = PoolType;
  451. classEntry->file = File;
  452. classEntry->line = Line;
  453. classEntry->numberOutstanding = 0;
  454. classEntry->bytesOutstanding = 0;
  455. classEntry->numberEverAllocated = 0;
  456. classEntry->bytesEverAllocated = 0;
  457. //
  458. // Put it in the tree of classes.
  459. //
  460. SipInsertElementTree(CountingMallocClassTree, classEntry, classKey);
  461. //
  462. // And put it in the list of classes.
  463. //
  464. classEntry->prev = CountingMallocClassListHead;
  465. classEntry->next = CountingMallocClassListHead->next;
  466. classEntry->prev->next = classEntry->next->prev = classEntry;
  467. }
  468. //
  469. // Roll up an entry for the pointer.
  470. //
  471. entry = ExAllocatePoolWithTag(NonPagedPool, sizeof(SIS_COUNTING_MALLOC_ENTRY), ' siS');
  472. if (NULL == entry) {
  473. CountingMallocInternalFailure = TRUE;
  474. KeReleaseSpinLock(CountingMallocLock, OldIrql);
  475. return memoryFromExAllocate;
  476. }
  477. //
  478. // Update the stats in the class.
  479. //
  480. classEntry->numberOutstanding++;
  481. classEntry->bytesOutstanding += NumberOfBytes;
  482. classEntry->numberEverAllocated++;
  483. classEntry->bytesEverAllocated += NumberOfBytes;
  484. //
  485. // Fill in the pointer entry.
  486. //
  487. entry->p = memoryFromExAllocate;
  488. entry->classEntry = classEntry;
  489. entry->byteCount = NumberOfBytes;
  490. //
  491. // Stick it in the tree.
  492. //
  493. key->p = memoryFromExAllocate;
  494. SipInsertElementTree(CountingMallocTree, entry, key);
  495. KeReleaseSpinLock(CountingMallocLock, OldIrql);
  496. return memoryFromExAllocate;
  497. }
  498. VOID
  499. CountingExFreePool(
  500. PVOID p)
  501. {
  502. SIS_COUNTING_MALLOC_KEY key[1];
  503. PSIS_COUNTING_MALLOC_ENTRY entry;
  504. KIRQL OldIrql;
  505. key->p = p;
  506. KeAcquireSpinLock(CountingMallocLock, &OldIrql);
  507. entry = SipLookupElementTree(CountingMallocTree, key);
  508. if (NULL == entry) {
  509. //
  510. // We may have failed to allocate the entry because of an
  511. // internal failure in the counting package, or else we're
  512. // freeing memory that was allocated by another system
  513. // component, like the SystemBuffer in an irp.
  514. //
  515. } else {
  516. //
  517. // Update the stats in the class.
  518. //
  519. ASSERT(entry->classEntry->numberOutstanding > 0);
  520. entry->classEntry->numberOutstanding--;
  521. ASSERT(entry->classEntry->bytesOutstanding >= entry->byteCount);
  522. entry->classEntry->bytesOutstanding -= entry->byteCount;
  523. //
  524. // Remove the entry from the tree
  525. //
  526. SipDeleteElementTree(CountingMallocTree, entry);
  527. //
  528. // And free it
  529. //
  530. ExFreePool(entry);
  531. }
  532. KeReleaseSpinLock(CountingMallocLock, OldIrql);
  533. //
  534. // Free the caller's memory
  535. //
  536. ExFreePool(p);
  537. }
  538. VOID
  539. InitCountingMalloc(void)
  540. {
  541. KeInitializeSpinLock(CountingMallocLock);
  542. CountingMallocClassListHead->next = CountingMallocClassListHead->prev = CountingMallocClassListHead;
  543. SipInitializeTree(CountingMallocClassTree, CountingMallocClassCompareRoutine);
  544. SipInitializeTree(CountingMallocTree, CountingMallocCompareRoutine);
  545. }
  546. VOID
  547. UnloadCountingMalloc(void)
  548. {
  549. SipDeleteTree(CountingMallocTree);
  550. SipDeleteTree(CountingMallocClassTree);
  551. }
  552. VOID
  553. DumpCountingMallocStats(void)
  554. {
  555. KIRQL OldIrql;
  556. PSIS_COUNTING_MALLOC_CLASS_ENTRY classEntry;
  557. ULONG totalAllocated = 0;
  558. ULONG totalEverAllocated = 0;
  559. ULONG totalBytesAllocated = 0;
  560. ULONG totalBytesEverAllocated = 0;
  561. //
  562. // Note that this function does NOT acquire CountingMallocLock,
  563. // so there can be no concurrent allocations/frees happening.
  564. // CountingMallocLock would raise to DPC irql,
  565. // and the filename strings might be pageable.
  566. //
  567. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_LEVEL,
  568. "Tag\tFile\tLine\tPoolType\tCountOutstanding\tBytesOutstanding\tTotalEverAllocated\tTotalBytesAllocated\n"));
  569. for (classEntry = CountingMallocClassListHead->next;
  570. classEntry != CountingMallocClassListHead;
  571. classEntry = classEntry->next) {
  572. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_LEVEL,
  573. "%c%c%c%c\t%s\t%d\t%s\t%d\t%d\t%d\t%d\n",
  574. (CHAR)(classEntry->tag >> 24),
  575. (CHAR)(classEntry->tag >> 16),
  576. (CHAR)(classEntry->tag >> 8),
  577. (CHAR)(classEntry->tag),
  578. classEntry->file,
  579. classEntry->line,
  580. (classEntry->poolType == NonPagedPool) ? "NonPagedPool" : ((classEntry->poolType == PagedPool) ? "PagedPool" : "Other"),
  581. classEntry->numberOutstanding,
  582. classEntry->bytesOutstanding,
  583. classEntry->numberEverAllocated,
  584. (ULONG)classEntry->bytesEverAllocated));
  585. totalAllocated += classEntry->numberOutstanding;
  586. totalEverAllocated += classEntry->numberEverAllocated;
  587. totalBytesAllocated += classEntry->bytesOutstanding;
  588. totalBytesEverAllocated += (ULONG)classEntry->bytesEverAllocated;
  589. }
  590. KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_LEVEL,
  591. "%d objects, %d bytes currently allocated. %d objects, %d bytes ever allocated.\n",
  592. totalAllocated,totalBytesAllocated,totalEverAllocated,totalBytesEverAllocated));
  593. }
  594. #endif // COUNTING_MALLOC