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.

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