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.

2561 lines
55 KiB

  1. /*++
  2. Copyright (c) 1998-2001 Microsoft Corporation
  3. Module Name:
  4. debug.c
  5. Abstract:
  6. This module contains debug support routines.
  7. Author:
  8. Keith Moore (keithmo) 10-Jun-1998
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #include "debugp.h"
  13. #if DBG
  14. #undef ExAllocatePool
  15. #undef ExFreePool
  16. //
  17. // Private constants.
  18. //
  19. #define NUM_THREAD_HASH_BUCKETS 31
  20. #define RANDOM_CONSTANT 314159269UL
  21. #define RANDOM_PRIME 1000000007UL
  22. #define HASH_SCRAMBLE(hash) \
  23. (ULONG_PTR)((((ULONG_PTR)(hash)) * (ULONG_PTR)RANDOM_CONSTANT) \
  24. % (ULONG_PTR)RANDOM_PRIME)
  25. #define HASH_FROM_THREAD(thrd) \
  26. ((ULONG)(HASH_SCRAMBLE(thrd) % (ULONG_PTR)NUM_THREAD_HASH_BUCKETS))
  27. #define SET_RESOURCE_OWNED_EXCLUSIVE( pLock ) \
  28. (pLock)->pExclusiveOwner = PsGetCurrentThread()
  29. #define SET_RESOURCE_NOT_OWNED_EXCLUSIVE( pLock ) \
  30. (pLock)->pPreviousOwner = (pLock)->pExclusiveOwner; \
  31. (pLock)->pExclusiveOwner = NULL
  32. #define SET_SPIN_LOCK_OWNED( pLock ) \
  33. do { \
  34. (pLock)->pOwnerThread = PsGetCurrentThread(); \
  35. (pLock)->OwnerProcessor = (ULONG)KeGetCurrentProcessorNumber(); \
  36. } while (FALSE)
  37. #define SET_SPIN_LOCK_NOT_OWNED( pLock ) \
  38. do { \
  39. (pLock)->pOwnerThread = NULL; \
  40. (pLock)->OwnerProcessor = (ULONG)-1L; \
  41. } while (FALSE)
  42. //
  43. // Private types.
  44. //
  45. typedef struct _UL_POOL_HEADER {
  46. PSTR pFileName;
  47. USHORT LineNumber;
  48. SIZE_T Size;
  49. ULONG Tag;
  50. } UL_POOL_HEADER, *PUL_POOL_HEADER;
  51. typedef struct _UL_THREAD_HASH_BUCKET
  52. {
  53. UL_SPIN_LOCK BucketSpinLock;
  54. LIST_ENTRY BucketListHead;
  55. } UL_THREAD_HASH_BUCKET, *PUL_THREAD_HASH_BUCKET;
  56. //
  57. // Private prototypes.
  58. //
  59. VOID
  60. UlpDbgUpdatePoolCounter(
  61. IN OUT PLARGE_INTEGER pAddend,
  62. IN SIZE_T Increment
  63. );
  64. PSTR
  65. UlpDbgFindFilePart(
  66. IN PSTR pPath
  67. );
  68. PUL_DEBUG_THREAD_DATA
  69. UlpDbgFindThread(
  70. BOOLEAN OkToCreate,
  71. PSTR pFileName,
  72. USHORT LineNumber
  73. );
  74. VOID
  75. UlpDbgDereferenceThread(
  76. IN PUL_DEBUG_THREAD_DATA pData
  77. REFERENCE_DEBUG_FORMAL_PARAMS
  78. );
  79. //
  80. // Private macros.
  81. //
  82. #define ULP_DBG_FIND_THREAD() \
  83. UlpDbgFindThread(FALSE, (PSTR)__FILE__, (USHORT)__LINE__)
  84. #define ULP_DBG_FIND_OR_CREATE_THREAD() \
  85. UlpDbgFindThread(TRUE, (PSTR)__FILE__, (USHORT)__LINE__)
  86. #define ULP_DBG_DEREFERENCE_THREAD(pData) \
  87. UlpDbgDereferenceThread((pData) REFERENCE_DEBUG_ACTUAL_PARAMS)
  88. #ifdef ALLOC_PRAGMA
  89. #if DBG
  90. #pragma alloc_text( INIT, UlDbgInitializeDebugData )
  91. #pragma alloc_text( PAGE, UlDbgTerminateDebugData )
  92. #pragma alloc_text( PAGE, UlDbgAcquireResourceExclusive )
  93. #pragma alloc_text( PAGE, UlDbgAcquireResourceShared )
  94. #pragma alloc_text( PAGE, UlDbgReleaseResource )
  95. #pragma alloc_text( PAGE, UlDbgConvertExclusiveToShared)
  96. // #pragma alloc_text( PAGE, UlDbgTryToAcquireResourceExclusive)
  97. #pragma alloc_text( PAGE, UlDbgResourceOwnedExclusive )
  98. #pragma alloc_text( PAGE, UlDbgResourceUnownedExclusive )
  99. #endif // DBG
  100. #if 0
  101. NOT PAGEABLE -- UlDbgAllocatePool
  102. NOT PAGEABLE -- UlDbgFreePool
  103. NOT PAGEABLE -- UlDbgInitializeSpinLock
  104. NOT PAGEABLE -- UlDbgAcquireSpinLock
  105. NOT PAGEABLE -- UlDbgReleaseSpinLock
  106. NOT PAGEABLE -- UlDbgAcquireSpinLockAtDpcLevel
  107. NOT PAGEABLE -- UlDbgReleaseSpinLockFromDpcLevel
  108. NOT PAGEABLE -- UlDbgSpinLockOwned
  109. NOT PAGEABLE -- UlDbgSpinLockUnowned
  110. NOT PAGEABLE -- UlDbgExceptionFilter
  111. NOT PAGEABLE -- UlDbgInvalidCompletionRoutine
  112. NOT PAGEABLE -- UlDbgStatus
  113. NOT PAGEABLE -- UlDbgEnterDriver
  114. NOT PAGEABLE -- UlDbgLeaveDriver
  115. NOT PAGEABLE -- UlDbgInitializeResource
  116. NOT PAGEABLE -- UlDbgDeleteResource
  117. NOT PAGEABLE -- UlDbgAllocateIrp
  118. NOT PAGEABLE -- UlDbgFreeIrp
  119. NOT PAGEABLE -- UlDbgCallDriver
  120. NOT PAGEABLE -- UlDbgCompleteRequest
  121. NOT PAGEABLE -- UlDbgAllocateMdl
  122. NOT PAGEABLE -- UlDbgFreeMdl
  123. NOT PAGEABLE -- UlpDbgUpdatePoolCounter
  124. NOT PAGEABLE -- UlpDbgFindFilePart
  125. NOT PAGEABLE -- UlpDbgFindThread
  126. NOT PAGEABLE -- UlpDbgDereferenceThread
  127. #endif
  128. #endif // ALLOC_PRAGMA
  129. //
  130. // Private globals.
  131. //
  132. UL_THREAD_HASH_BUCKET g_DbgThreadHashBuckets[NUM_THREAD_HASH_BUCKETS];
  133. LONG g_DbgThreadCreated;
  134. LONG g_DbgThreadDestroyed;
  135. UL_SPIN_LOCK g_DbgSpinLock;
  136. LIST_ENTRY g_DbgGlobalResourceListHead;
  137. //
  138. // Public functions.
  139. //
  140. /***************************************************************************++
  141. Routine Description:
  142. Initializes global debug-specific data.
  143. --***************************************************************************/
  144. VOID
  145. UlDbgInitializeDebugData(
  146. VOID
  147. )
  148. {
  149. ULONG i;
  150. CHAR spinLockName[sizeof("g_DbgThreadHashBuckets[00000].BucketSpinLock")];
  151. //
  152. // Initialize the lock lists.
  153. //
  154. UlInitializeSpinLock( &g_DbgSpinLock, "g_DbgSpinLock" );
  155. InitializeListHead( &g_DbgGlobalResourceListHead );
  156. //
  157. // Initialize the thread hash buckets.
  158. //
  159. for (i = 0 ; i < NUM_THREAD_HASH_BUCKETS ; i++)
  160. {
  161. sprintf(
  162. spinLockName,
  163. "g_DbgThreadHashBuckets[%lu].BucketSpinLock",
  164. i
  165. );
  166. UlInitializeSpinLock(
  167. &g_DbgThreadHashBuckets[i].BucketSpinLock,
  168. spinLockName
  169. );
  170. InitializeListHead(
  171. &g_DbgThreadHashBuckets[i].BucketListHead
  172. );
  173. }
  174. } // UlDbgInitializeDebugData
  175. /***************************************************************************++
  176. Routine Description:
  177. Undoes any initialization performed in UlDbgInitializeDebugData().
  178. --***************************************************************************/
  179. VOID
  180. UlDbgTerminateDebugData(
  181. VOID
  182. )
  183. {
  184. ULONG i;
  185. //
  186. // Ensure the thread hash buckets are empty.
  187. //
  188. for (i = 0 ; i < NUM_THREAD_HASH_BUCKETS ; i++)
  189. {
  190. ASSERT( IsListEmpty( &g_DbgThreadHashBuckets[i].BucketListHead ) );
  191. }
  192. //
  193. // Ensure the lock lists are empty.
  194. //
  195. ASSERT( IsListEmpty( &g_DbgGlobalResourceListHead ) );
  196. } // UlDbgTerminateDebugData
  197. /***************************************************************************++
  198. Routine Description:
  199. Prettyprints a buffer to DbgPrint output. More or less turns it back
  200. into a C-style string.
  201. CODEWORK: produce a Unicode version of this helper function
  202. Arguments:
  203. Buffer - Buffer to prettyprint
  204. BufferSize - number of bytes to prettyprint
  205. Return Value:
  206. ULONG - number of prettyprinted characters sent to DbgPrint,
  207. excluding inserted newlines.
  208. --***************************************************************************/
  209. ULONG
  210. UlDbgPrettyPrintBuffer(
  211. const UCHAR* pBuffer,
  212. ULONG BufferSize
  213. )
  214. {
  215. int i;
  216. CHAR OutputBuffer[200];
  217. PCHAR pOut = OutputBuffer;
  218. BOOLEAN CrLfNeeded = FALSE, JustCrLfd = FALSE;
  219. ULONG cch = 0;
  220. if (pBuffer == NULL || BufferSize == 0)
  221. return 0;
  222. for (i = 0; i < (int)BufferSize; ++i)
  223. {
  224. UCHAR ch = pBuffer[i];
  225. if ('\r' == ch) // CR
  226. {
  227. *pOut++ = '\\'; *pOut++ = 'r';
  228. if (i + 1 == BufferSize || pBuffer[i + 1] != '\n')
  229. CrLfNeeded = TRUE;
  230. }
  231. else if ('\n' == ch) // LF
  232. {
  233. *pOut++ = '\\'; *pOut++ = 'n';
  234. CrLfNeeded = TRUE;
  235. }
  236. else if ('\t' == ch) // TAB
  237. {
  238. *pOut++ = '\\'; *pOut++ = 't';
  239. }
  240. else if ('\0' == ch) // NUL
  241. {
  242. *pOut++ = '\\'; *pOut++ = '0';
  243. }
  244. else if ('\\' == ch) // \ (backslash)
  245. {
  246. *pOut++ = '\\'; *pOut++ = '\\';
  247. }
  248. else if (ch < 0x20 || ch == 127) // control chars
  249. {
  250. const UCHAR HexString[] = "0123456789abcdef";
  251. *pOut++ = '\\'; *pOut++ = 'x';
  252. *pOut++ = HexString[ch >> 4]; *pOut++ = HexString[ch & 0xf];
  253. }
  254. else
  255. {
  256. *pOut++ = ch;
  257. }
  258. if (pOut - OutputBuffer >= sizeof(OutputBuffer) - 4) // strlen("\xAB")
  259. CrLfNeeded = TRUE;
  260. if (CrLfNeeded)
  261. {
  262. *pOut++ = '\n'; *pOut = '\0';
  263. DbgPrint(OutputBuffer);
  264. cch += (ULONG) (pOut - 1 - OutputBuffer);
  265. pOut = OutputBuffer;
  266. CrLfNeeded = FALSE;
  267. JustCrLfd = TRUE;
  268. }
  269. else
  270. {
  271. JustCrLfd = FALSE;
  272. }
  273. }
  274. if (! JustCrLfd)
  275. {
  276. *pOut++ = '\n'; *pOut = '\0';
  277. DbgPrint(OutputBuffer);
  278. cch += (ULONG) (pOut - 1 - OutputBuffer);
  279. }
  280. return cch;
  281. } // UlDbgPrettyPrintBuffer
  282. /***************************************************************************++
  283. Routine Description:
  284. Debug memory allocator. Allocates a block of pool with a header
  285. containing the filename & line number of the caller, plus the
  286. tag for the data.
  287. Arguments:
  288. PoolType - Supplies the pool to allocate from. Must be either
  289. NonPagedPool, NonPagedPoolMustSucceed, or PagedPool.
  290. NumberOfBytes - Supplies the number of bytes to allocate.
  291. Tag - Supplies a four-byte tag for the pool block. Useful for
  292. debugging leaks.
  293. pFileName - Supplies the filename of the caller.
  294. function.
  295. LineNumber - Supplies the line number of the caller.
  296. Return Value:
  297. PVOID - Pointer to the allocated block if successful, NULL otherwise.
  298. --***************************************************************************/
  299. PVOID
  300. UlDbgAllocatePool(
  301. IN POOL_TYPE PoolType,
  302. IN SIZE_T NumberOfBytes,
  303. IN ULONG Tag,
  304. IN PSTR pFileName,
  305. IN USHORT LineNumber
  306. )
  307. {
  308. PUL_POOL_HEADER pHeader;
  309. //
  310. // Sanity check.
  311. //
  312. ASSERT( PoolType == NonPagedPool ||
  313. PoolType == NonPagedPoolMustSucceed ||
  314. PoolType == NonPagedPoolCacheAligned ||
  315. PoolType == PagedPool );
  316. ASSERT( IS_VALID_TAG( Tag ) );
  317. //
  318. // Allocate the block with additional space for the header.
  319. //
  320. pHeader = (PUL_POOL_HEADER)(
  321. ExAllocatePoolWithTag(
  322. PoolType,
  323. NumberOfBytes + sizeof(*pHeader),
  324. Tag
  325. )
  326. );
  327. if (pHeader == NULL)
  328. {
  329. return NULL;
  330. }
  331. //
  332. // Initialize the header.
  333. //
  334. pHeader->pFileName = pFileName;
  335. pHeader->LineNumber = LineNumber;
  336. pHeader->Size = NumberOfBytes;
  337. pHeader->Tag = Tag;
  338. //
  339. // Fill the body with garbage.
  340. //
  341. RtlFillMemory( (PVOID)(pHeader + 1), NumberOfBytes, '\xcc' );
  342. //
  343. // Update the statistics.
  344. //
  345. InterlockedIncrement(
  346. &g_UlDebugStats.TotalAllocations
  347. );
  348. UlpDbgUpdatePoolCounter(
  349. &g_UlDebugStats.TotalBytesAllocated,
  350. NumberOfBytes
  351. );
  352. //
  353. // Return a pointer to the body.
  354. //
  355. return (PVOID)(pHeader + 1);
  356. } // UlDbgAllocatePool
  357. /***************************************************************************++
  358. Routine Description:
  359. Frees memory allocated by UlDbgAllocatePool(), ensuring that the tags
  360. match.
  361. Arguments:
  362. pPointer - Supplies a pointer to the pool block to free.
  363. Tag - Supplies the tag for the block to be freed. If the supplied
  364. tag does not match the tag of the allocated block, an assertion
  365. failure is generated.
  366. --***************************************************************************/
  367. VOID
  368. UlDbgFreePool(
  369. IN PVOID pPointer,
  370. IN ULONG Tag
  371. )
  372. {
  373. PUL_POOL_HEADER pHeader;
  374. //
  375. // Get a pointer to the header.
  376. //
  377. pHeader = (PUL_POOL_HEADER)pPointer - 1;
  378. //
  379. // Update the statistics.
  380. //
  381. InterlockedIncrement(
  382. &g_UlDebugStats.TotalFrees
  383. );
  384. UlpDbgUpdatePoolCounter(
  385. &g_UlDebugStats.TotalBytesFreed,
  386. pHeader->Size
  387. );
  388. //
  389. // Validate the tag.
  390. //
  391. if( pHeader->Tag == Tag )
  392. {
  393. ASSERT( IS_VALID_TAG( Tag ) );
  394. pHeader->Tag = MAKE_FREE_TAG( Tag );
  395. }
  396. else
  397. {
  398. ASSERT( !"Invalid tag" );
  399. }
  400. //
  401. // Actually free the block.
  402. //
  403. MyFreePoolWithTag(
  404. (PVOID)pHeader,
  405. Tag
  406. );
  407. } // UlDbgFreePool
  408. /***************************************************************************++
  409. Routine Description:
  410. Initializes an instrumented spinlock.
  411. --***************************************************************************/
  412. VOID
  413. UlDbgInitializeSpinLock(
  414. IN PUL_SPIN_LOCK pSpinLock,
  415. IN PSTR pSpinLockName,
  416. IN PSTR pFileName,
  417. IN USHORT LineNumber
  418. )
  419. {
  420. //
  421. // Initialize the spinlock.
  422. //
  423. RtlZeroMemory( pSpinLock, sizeof(*pSpinLock) );
  424. pSpinLock->pSpinLockName = pSpinLockName;
  425. KeInitializeSpinLock( &pSpinLock->KSpinLock );
  426. SET_SPIN_LOCK_NOT_OWNED( pSpinLock );
  427. //
  428. // Update the global statistics.
  429. //
  430. InterlockedIncrement( &g_UlDebugStats.TotalSpinLocksInitialized );
  431. } // UlDbgInitializeSpinLock
  432. /***************************************************************************++
  433. Routine Description:
  434. Acquires an instrumented spinlock.
  435. --***************************************************************************/
  436. VOID
  437. UlDbgAcquireSpinLock(
  438. IN PUL_SPIN_LOCK pSpinLock,
  439. OUT PKIRQL pOldIrql,
  440. IN PSTR pFileName,
  441. IN USHORT LineNumber
  442. )
  443. {
  444. //
  445. // Sanity check.
  446. //
  447. ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
  448. //
  449. // Acquire the lock.
  450. //
  451. KeAcquireSpinLock(
  452. &pSpinLock->KSpinLock,
  453. pOldIrql
  454. );
  455. //
  456. // Mark it as owned by the current thread.
  457. //
  458. ASSERT( UlDbgSpinLockUnowned( pSpinLock ) );
  459. SET_SPIN_LOCK_OWNED( pSpinLock );
  460. //
  461. // Update the statistics.
  462. //
  463. pSpinLock->Acquisitions++;
  464. pSpinLock->pLastAcquireFileName = pFileName;
  465. pSpinLock->LastAcquireLineNumber = LineNumber;
  466. InterlockedIncrement(
  467. &g_UlDebugStats.TotalAcquisitions
  468. );
  469. } // UlDbgAcquireSpinLock
  470. /***************************************************************************++
  471. Routine Description:
  472. Releases an instrumented spinlock.
  473. --***************************************************************************/
  474. VOID
  475. UlDbgReleaseSpinLock(
  476. IN PUL_SPIN_LOCK pSpinLock,
  477. IN KIRQL OldIrql,
  478. IN PSTR pFileName,
  479. IN USHORT LineNumber
  480. )
  481. {
  482. //
  483. // Mark it as unowned.
  484. //
  485. ASSERT( UlDbgSpinLockOwned( pSpinLock ) );
  486. SET_SPIN_LOCK_NOT_OWNED( pSpinLock );
  487. //
  488. // Update the statistics.
  489. //
  490. InterlockedIncrement(
  491. &g_UlDebugStats.TotalReleases
  492. );
  493. pSpinLock->Releases++;
  494. pSpinLock->pLastReleaseFileName = pFileName;
  495. pSpinLock->LastReleaseLineNumber = LineNumber;
  496. //
  497. // Release the lock.
  498. //
  499. KeReleaseSpinLock(
  500. &pSpinLock->KSpinLock,
  501. OldIrql
  502. );
  503. } // UlDbgReleaseSpinLock
  504. /***************************************************************************++
  505. Routine Description:
  506. Acquires an instrumented spinlock while running at DPC level.
  507. --***************************************************************************/
  508. VOID
  509. UlDbgAcquireSpinLockAtDpcLevel(
  510. IN PUL_SPIN_LOCK pSpinLock,
  511. IN PSTR pFileName,
  512. IN USHORT LineNumber
  513. )
  514. {
  515. //
  516. // Sanity check.
  517. //
  518. ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
  519. //
  520. // Acquire the lock.
  521. //
  522. KeAcquireSpinLockAtDpcLevel(
  523. &pSpinLock->KSpinLock
  524. );
  525. //
  526. // Mark it as owned by the current thread.
  527. //
  528. ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
  529. SET_SPIN_LOCK_OWNED( pSpinLock );
  530. //
  531. // Update the statistics.
  532. //
  533. pSpinLock->AcquisitionsAtDpcLevel++;
  534. pSpinLock->pLastAcquireFileName = pFileName;
  535. pSpinLock->LastAcquireLineNumber = LineNumber;
  536. InterlockedIncrement(
  537. &g_UlDebugStats.TotalAcquisitionsAtDpcLevel
  538. );
  539. } // UlDbgAcquireSpinLockAtDpcLevel
  540. /***************************************************************************++
  541. Routine Description:
  542. Releases an instrumented spinlock acquired at DPC level.
  543. --***************************************************************************/
  544. VOID
  545. UlDbgReleaseSpinLockFromDpcLevel(
  546. IN PUL_SPIN_LOCK pSpinLock,
  547. IN PSTR pFileName,
  548. IN USHORT LineNumber
  549. )
  550. {
  551. //
  552. // Mark it as unowned.
  553. //
  554. ASSERT( UlDbgSpinLockOwned( pSpinLock ) );
  555. SET_SPIN_LOCK_NOT_OWNED( pSpinLock );
  556. //
  557. // Update the statistics.
  558. //
  559. InterlockedIncrement(
  560. &g_UlDebugStats.TotalReleasesFromDpcLevel
  561. );
  562. pSpinLock->ReleasesFromDpcLevel++;
  563. pSpinLock->pLastReleaseFileName = pFileName;
  564. pSpinLock->LastReleaseLineNumber = LineNumber;
  565. //
  566. // Release the lock.
  567. //
  568. KeReleaseSpinLockFromDpcLevel(
  569. &pSpinLock->KSpinLock
  570. );
  571. } // UlDbgReleaseSpinLockAtDpcLevel
  572. /***************************************************************************++
  573. Routine Description:
  574. Acquires an instrumented in-stack-queue spinlock.
  575. --***************************************************************************/
  576. VOID
  577. UlDbgAcquireInStackQueuedSpinLock(
  578. IN PUL_SPIN_LOCK pSpinLock,
  579. OUT PKLOCK_QUEUE_HANDLE pLockHandle,
  580. IN PSTR pFileName,
  581. IN USHORT LineNumber
  582. )
  583. {
  584. //
  585. // Sanity check.
  586. //
  587. ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
  588. //
  589. // Acquire the lock.
  590. //
  591. KeAcquireInStackQueuedSpinLock(
  592. &pSpinLock->KSpinLock,
  593. pLockHandle
  594. );
  595. //
  596. // Mark it as owned by the current thread.
  597. //
  598. ASSERT( UlDbgSpinLockUnowned( pSpinLock ) );
  599. SET_SPIN_LOCK_OWNED( pSpinLock );
  600. //
  601. // Update the statistics.
  602. //
  603. pSpinLock->Acquisitions++;
  604. pSpinLock->pLastAcquireFileName = pFileName;
  605. pSpinLock->LastAcquireLineNumber = LineNumber;
  606. InterlockedIncrement(
  607. &g_UlDebugStats.TotalAcquisitions
  608. );
  609. } // UlDbgAcquireInStackQueuedSpinLock
  610. /***************************************************************************++
  611. Routine Description:
  612. Releases an instrumented in-stack-queue spinlock.
  613. --***************************************************************************/
  614. VOID
  615. UlDbgReleaseInStackQueuedSpinLock(
  616. IN PUL_SPIN_LOCK pSpinLock,
  617. IN PKLOCK_QUEUE_HANDLE pLockHandle,
  618. IN PSTR pFileName,
  619. IN USHORT LineNumber
  620. )
  621. {
  622. //
  623. // Mark it as unowned.
  624. //
  625. ASSERT( UlDbgSpinLockOwned( pSpinLock ) );
  626. SET_SPIN_LOCK_NOT_OWNED( pSpinLock );
  627. //
  628. // Update the statistics.
  629. //
  630. InterlockedIncrement(
  631. &g_UlDebugStats.TotalReleases
  632. );
  633. pSpinLock->Releases++;
  634. pSpinLock->pLastReleaseFileName = pFileName;
  635. pSpinLock->LastReleaseLineNumber = LineNumber;
  636. //
  637. // Release the lock.
  638. //
  639. KeReleaseInStackQueuedSpinLock(
  640. pLockHandle
  641. );
  642. } // UlDbgReleaseInStackQueuedSpinLock
  643. /***************************************************************************++
  644. Routine Description:
  645. Acquires an instrumented in-stack-queue spinlock while running at DPC level.
  646. --***************************************************************************/
  647. VOID
  648. UlDbgAcquireSpinLockAtDpcLevel(
  649. IN PUL_SPIN_LOCK pSpinLock,
  650. OUT PKLOCK_QUEUE_HANDLE pLockHandle,
  651. IN PSTR pFileName,
  652. IN USHORT LineNumber
  653. )
  654. {
  655. //
  656. // Sanity check.
  657. //
  658. ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
  659. //
  660. // Acquire the lock.
  661. //
  662. KeAcquireInStackQueuedSpinLockAtDpcLevel(
  663. &pSpinLock->KSpinLock,
  664. pLockHandle
  665. );
  666. //
  667. // Mark it as owned by the current thread.
  668. //
  669. ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
  670. SET_SPIN_LOCK_OWNED( pSpinLock );
  671. //
  672. // Update the statistics.
  673. //
  674. pSpinLock->AcquisitionsAtDpcLevel++;
  675. pSpinLock->pLastAcquireFileName = pFileName;
  676. pSpinLock->LastAcquireLineNumber = LineNumber;
  677. InterlockedIncrement(
  678. &g_UlDebugStats.TotalAcquisitionsAtDpcLevel
  679. );
  680. } // UlDbgAcquireInStackQueuedSpinLockAtDpcLevel
  681. /***************************************************************************++
  682. Routine Description:
  683. Releases an instrumented in-stack-queue spinlock acquired at DPC level.
  684. --***************************************************************************/
  685. VOID
  686. UlDbgReleaseSpinLockFromDpcLevel(
  687. IN PUL_SPIN_LOCK pSpinLock,
  688. IN PKLOCK_QUEUE_HANDLE pLockHandle,
  689. IN PSTR pFileName,
  690. IN USHORT LineNumber
  691. )
  692. {
  693. //
  694. // Mark it as unowned.
  695. //
  696. ASSERT( UlDbgSpinLockOwned( pSpinLock ) );
  697. SET_SPIN_LOCK_NOT_OWNED( pSpinLock );
  698. //
  699. // Update the statistics.
  700. //
  701. InterlockedIncrement(
  702. &g_UlDebugStats.TotalReleasesFromDpcLevel
  703. );
  704. pSpinLock->ReleasesFromDpcLevel++;
  705. pSpinLock->pLastReleaseFileName = pFileName;
  706. pSpinLock->LastReleaseLineNumber = LineNumber;
  707. //
  708. // Release the lock.
  709. //
  710. KeReleaseInStackQueuedSpinLockFromDpcLevel(
  711. pLockHandle
  712. );
  713. } // UlDbgReleaseInStackQueuedSpinLockFromDpcLevel
  714. /***************************************************************************++
  715. Routine Description:
  716. Determines if the specified spinlock is owned by the current thread.
  717. Arguments:
  718. pSpinLock - Supplies the spinlock to test.
  719. Return Value:
  720. BOOLEAN - TRUE if the spinlock is owned by the current thread, FALSE
  721. otherwise.
  722. --***************************************************************************/
  723. BOOLEAN
  724. UlDbgSpinLockOwned(
  725. IN PUL_SPIN_LOCK pSpinLock
  726. )
  727. {
  728. if (pSpinLock->pOwnerThread == PsGetCurrentThread())
  729. {
  730. ASSERT( pSpinLock->OwnerProcessor == (ULONG)KeGetCurrentProcessorNumber() );
  731. return TRUE;
  732. }
  733. return FALSE;
  734. } // UlDbgSpinLockOwned
  735. /***************************************************************************++
  736. Routine Description:
  737. Determines if the specified spinlock is unowned.
  738. Arguments:
  739. pSpinLock - Supplies the spinlock to test.
  740. Return Value:
  741. BOOLEAN - TRUE if the spinlock is unowned, FALSE otherwise.
  742. --***************************************************************************/
  743. BOOLEAN
  744. UlDbgSpinLockUnowned(
  745. IN PUL_SPIN_LOCK pSpinLock
  746. )
  747. {
  748. if (pSpinLock->pOwnerThread == NULL)
  749. {
  750. return TRUE;
  751. }
  752. return FALSE;
  753. } // UlDbgSpinLockUnowned
  754. /***************************************************************************++
  755. Routine Description:
  756. Filter for exceptions caught with try/except.
  757. Arguments:
  758. pExceptionPointers - Supplies information identifying the source
  759. and type of exception raised.
  760. pFileName - Supplies the name of the file generating the exception.
  761. LineNumber - Supplies the line number of the exception filter that
  762. caught the exception.
  763. Return Value:
  764. LONG - Should always be EXCEPTION_EXECUTE_HANDLER
  765. --***************************************************************************/
  766. LONG
  767. UlDbgExceptionFilter(
  768. IN PEXCEPTION_POINTERS pExceptionPointers,
  769. IN PSTR pFileName,
  770. IN USHORT LineNumber
  771. )
  772. {
  773. //
  774. // Protect ourselves just in case the process is completely messed up.
  775. //
  776. __try
  777. {
  778. //
  779. // Whine about it.
  780. //
  781. KdPrint((
  782. "UlDbgExceptionFilter: exception %08lx @ %p, caught in %s:%d\n",
  783. pExceptionPointers->ExceptionRecord->ExceptionCode,
  784. pExceptionPointers->ExceptionRecord->ExceptionAddress,
  785. UlpDbgFindFilePart( pFileName ),
  786. LineNumber
  787. ));
  788. }
  789. __except( EXCEPTION_EXECUTE_HANDLER )
  790. {
  791. //
  792. // Not much we can do here...
  793. //
  794. NOTHING;
  795. }
  796. return EXCEPTION_EXECUTE_HANDLER;
  797. } // UlDbgExceptionFilter
  798. /***************************************************************************++
  799. Routine Description:
  800. Sometimes it's not acceptable to proceed with warnings ( as status ) after
  801. we caught an exception. I.e. Caught an missaligned warning during sendresponse
  802. and called the IoCompleteRequest with status misaligned. This will cause Io
  803. Manager to complete request to port, even though we don't want it to happen.
  804. In that case we have to carefully replace warnings with a generic error.
  805. Arguments:
  806. pExceptionPointers - Supplies information identifying the source
  807. and type of exception raised.
  808. pFileName - Supplies the name of the file generating the exception.
  809. LineNumber - Supplies the line number of the exception filter that
  810. caught the exception.
  811. Return Value:
  812. NTSTATUS - Converted error value : UL_DEFAULT_ERROR_ON_EXCEPTION
  813. --***************************************************************************/
  814. NTSTATUS
  815. UlDbgConvertExceptionCode(
  816. IN NTSTATUS status,
  817. IN PSTR pFileName,
  818. IN USHORT LineNumber
  819. )
  820. {
  821. //
  822. // Whine about it.
  823. //
  824. KdPrint((
  825. "UlDbgConvertExceptionCode: exception %08lx converted to %08lx, at %s:%d\n",
  826. status,
  827. UL_DEFAULT_ERROR_ON_EXCEPTION,
  828. UlpDbgFindFilePart( pFileName ),
  829. LineNumber
  830. ));
  831. return UL_DEFAULT_ERROR_ON_EXCEPTION;
  832. }
  833. /***************************************************************************++
  834. Routine Description:
  835. Completion handler for incomplete IRP contexts.
  836. Arguments:
  837. pCompletionContext - Supplies an uninterpreted context value
  838. as passed to the asynchronous API.
  839. Status - Supplies the final completion status of the
  840. asynchronous API.
  841. Information - Optionally supplies additional information about
  842. the completed operation, such as the number of bytes
  843. transferred.
  844. --***************************************************************************/
  845. VOID
  846. UlDbgInvalidCompletionRoutine(
  847. IN PVOID pCompletionContext,
  848. IN NTSTATUS Status,
  849. IN ULONG_PTR Information
  850. )
  851. {
  852. DbgPrint(
  853. "UlDbgInvalidCompletionRoutine called!\n"
  854. " pCompletionContext = %p\n"
  855. " Status = %08lx\n"
  856. " Information = %p\n",
  857. pCompletionContext,
  858. Status,
  859. Information
  860. );
  861. ASSERT( !"UlDbgInvalidCompletionRoutine called!" );
  862. } // UlDbgInvalidCompletionRoutine
  863. /***************************************************************************++
  864. Routine Description:
  865. Hook for catching failed operations. This routine is called within each
  866. routine with the completion status.
  867. Arguments:
  868. Status - Supplies the completion status.
  869. pFileName - Supplies the filename of the caller.
  870. LineNumber - Supplies the line number of the caller.
  871. Return Value:
  872. NTSTATUS - Completion status.
  873. --***************************************************************************/
  874. NTSTATUS
  875. UlDbgStatus(
  876. IN NTSTATUS Status,
  877. IN PSTR pFileName,
  878. IN USHORT LineNumber
  879. )
  880. {
  881. //
  882. // paulmcd: ignore STATUS_END_OF_FILE. this is a non-fatal return value
  883. //
  884. if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
  885. {
  886. if (g_UlVerboseErrors)
  887. {
  888. DbgPrint(
  889. "UlDbgStatus: %s:%lu returning %08lx\n",
  890. UlpDbgFindFilePart( pFileName ),
  891. LineNumber,
  892. Status
  893. );
  894. }
  895. if (g_UlBreakOnError)
  896. {
  897. DbgBreakPoint();
  898. }
  899. }
  900. return Status;
  901. } // UlDbgStatus
  902. /***************************************************************************++
  903. Routine Description:
  904. Routine invoked upon entry into the driver.
  905. Arguments:
  906. pFunctionName - Supplies the name of the function used to enter
  907. the driver.
  908. pIrp - Supplies an optional IRP to log.
  909. pFileName - Supplies the filename of the caller.
  910. LineNumber - Supplies the line number of the caller.
  911. --***************************************************************************/
  912. VOID
  913. UlDbgEnterDriver(
  914. IN PSTR pFunctionName,
  915. IN PIRP pIrp OPTIONAL,
  916. IN PSTR pFileName,
  917. IN USHORT LineNumber
  918. )
  919. {
  920. PUL_DEBUG_THREAD_DATA pData;
  921. //
  922. // Log the IRP.
  923. //
  924. if (pIrp != NULL)
  925. {
  926. WRITE_IRP_TRACE_LOG(
  927. g_pIrpTraceLog,
  928. IRP_ACTION_INCOMING_IRP,
  929. pIrp,
  930. pFileName,
  931. LineNumber
  932. );
  933. }
  934. //
  935. // Find/create an entry for the current thread.
  936. //
  937. pData = ULP_DBG_FIND_OR_CREATE_THREAD();
  938. if (pData != NULL)
  939. {
  940. //
  941. // This should be the first time we enter the driver
  942. // unless we are stealing this thread due to an interrupt,
  943. // or we are calling another driver and they are calling
  944. // our completion routine in-line.
  945. //
  946. ASSERT( KeGetCurrentIrql() > PASSIVE_LEVEL ||
  947. pData->ExternalCallCount > 0 ||
  948. pData->ResourceCount == 0 );
  949. }
  950. } // UlDbgEnterDriver
  951. /***************************************************************************++
  952. Routine Description:
  953. Routine invoked upon exit from the driver.
  954. Arguments:
  955. pFunctionName - Supplies the name of the function used to enter
  956. the driver.
  957. pFileName - Supplies the filename of the caller.
  958. LineNumber - Supplies the line number of the caller.
  959. --***************************************************************************/
  960. VOID
  961. UlDbgLeaveDriver(
  962. IN PSTR pFunctionName,
  963. IN PSTR pFileName,
  964. IN USHORT LineNumber
  965. )
  966. {
  967. PUL_DEBUG_THREAD_DATA pData;
  968. //
  969. // Find an existing entry for the current thread.
  970. //
  971. pData = ULP_DBG_FIND_THREAD();
  972. if (pData != NULL)
  973. {
  974. //
  975. // Ensure no resources are acquired, then kill the thread data.
  976. //
  977. // we might have a resource acquired if we borrowed the thread
  978. // due to an interrupt.
  979. //
  980. // N.B. We dereference the thread data twice: once for the
  981. // call to ULP_DBG_FIND_THREAD() above, once for the call
  982. // made when entering the driver.
  983. //
  984. ASSERT( KeGetCurrentIrql() > PASSIVE_LEVEL ||
  985. pData->ExternalCallCount > 0 ||
  986. pData->ResourceCount == 0 );
  987. ASSERT( pData->ReferenceCount >= 2 );
  988. ULP_DBG_DEREFERENCE_THREAD( pData );
  989. ULP_DBG_DEREFERENCE_THREAD( pData );
  990. }
  991. } // UlDbgLeaveDriver
  992. /***************************************************************************++
  993. Routine Description:
  994. Initialize an instrumented resource.
  995. Arguments:
  996. pResource - Supplies the resource to initialize.
  997. pResourceName - Supplies a display name for the resource.
  998. Parameter - Supplies a ULONG_PTR parameter passed into sprintf()
  999. when creating the resource name.
  1000. pFileName - Supplies the filename of the caller.
  1001. LineNumber - Supplies the line number of the caller.
  1002. Return Value:
  1003. NTSTATUS - Completion status.
  1004. --***************************************************************************/
  1005. NTSTATUS
  1006. UlDbgInitializeResource(
  1007. IN PUL_ERESOURCE pResource,
  1008. IN PSTR pResourceName,
  1009. IN ULONG_PTR Parameter,
  1010. IN ULONG OwnerTag,
  1011. IN PSTR pFileName,
  1012. IN USHORT LineNumber
  1013. )
  1014. {
  1015. NTSTATUS status;
  1016. KIRQL oldIrql;
  1017. //
  1018. // Initialize the resource.
  1019. //
  1020. status = ExInitializeResource( &pResource->Resource );
  1021. if (NT_SUCCESS(status))
  1022. {
  1023. pResource->ExclusiveCount = 0;
  1024. pResource->SharedCount = 0;
  1025. pResource->ReleaseCount = 0;
  1026. pResource->OwnerTag = OwnerTag;
  1027. _snprintf(
  1028. (char*) pResource->ResourceName,
  1029. sizeof(pResource->ResourceName) - 1,
  1030. pResourceName,
  1031. Parameter
  1032. );
  1033. pResource->ResourceName[sizeof(pResource->ResourceName) - 1] = '\0';
  1034. SET_RESOURCE_NOT_OWNED_EXCLUSIVE( pResource );
  1035. //
  1036. // Put it on the global list.
  1037. //
  1038. UlAcquireSpinLock( &g_DbgSpinLock, &oldIrql );
  1039. InsertHeadList(
  1040. &g_DbgGlobalResourceListHead,
  1041. &pResource->GlobalResourceListEntry
  1042. );
  1043. UlReleaseSpinLock( &g_DbgSpinLock, oldIrql );
  1044. }
  1045. else
  1046. {
  1047. pResource->GlobalResourceListEntry.Flink = NULL;
  1048. }
  1049. return status;
  1050. } // UlDbgInitializeResource
  1051. /***************************************************************************++
  1052. Routine Description:
  1053. Deletes an instrumented resource.
  1054. Arguments:
  1055. pResource - Supplies the resource to delete.
  1056. pFileName - Supplies the filename of the caller.
  1057. LineNumber - Supplies the line number of the caller.
  1058. Return Value:
  1059. NTSTATUS - Completion status.
  1060. --***************************************************************************/
  1061. NTSTATUS
  1062. UlDbgDeleteResource(
  1063. IN PUL_ERESOURCE pResource,
  1064. IN PSTR pFileName,
  1065. IN USHORT LineNumber
  1066. )
  1067. {
  1068. NTSTATUS status;
  1069. KIRQL oldIrql;
  1070. PETHREAD pExclusiveOwner;
  1071. //
  1072. // Sanity check.
  1073. //
  1074. pExclusiveOwner = pResource->pExclusiveOwner;
  1075. if (pExclusiveOwner != NULL)
  1076. {
  1077. DbgBreakPoint();
  1078. DbgPrint(
  1079. "Resource %p [%s] owned by thread %p\n",
  1080. pResource,
  1081. pResource->ResourceName,
  1082. pExclusiveOwner
  1083. );
  1084. DbgBreakPoint();
  1085. }
  1086. // ASSERT( UlDbgResourceUnownedExclusive( pResource ) );
  1087. //
  1088. // Delete the resource.
  1089. //
  1090. status = ExDeleteResource( &pResource->Resource );
  1091. //
  1092. // Remove it from the global list.
  1093. //
  1094. if (pResource->GlobalResourceListEntry.Flink != NULL)
  1095. {
  1096. UlAcquireSpinLock( &g_DbgSpinLock, &oldIrql );
  1097. RemoveEntryList( &pResource->GlobalResourceListEntry );
  1098. UlReleaseSpinLock( &g_DbgSpinLock, oldIrql );
  1099. }
  1100. return status;
  1101. } // UlDbgDeleteResource
  1102. /***************************************************************************++
  1103. Routine Description:
  1104. Acquires exclusive access to an instrumented resource.
  1105. Arguments:
  1106. pResource - Supplies the resource to acquire.
  1107. Wait - Supplies TRUE if the thread should block waiting for the
  1108. resource.
  1109. pFileName - Supplies the filename of the caller.
  1110. LineNumber - Supplies the line number of the caller.
  1111. Return Value:
  1112. BOOLEAN - Completion status.
  1113. --***************************************************************************/
  1114. BOOLEAN
  1115. UlDbgAcquireResourceExclusive(
  1116. IN PUL_ERESOURCE pResource,
  1117. IN BOOLEAN Wait,
  1118. IN PSTR pFileName,
  1119. IN USHORT LineNumber
  1120. )
  1121. {
  1122. PUL_DEBUG_THREAD_DATA pData;
  1123. BOOLEAN result;
  1124. //
  1125. // Sanity check.
  1126. //
  1127. ASSERT(pResource);
  1128. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  1129. //
  1130. // Find an existing entry for the current thread.
  1131. //
  1132. pData = ULP_DBG_FIND_THREAD();
  1133. if (pData != NULL)
  1134. {
  1135. //
  1136. // Update the resource count.
  1137. //
  1138. pData->ResourceCount++;
  1139. ASSERT( pData->ResourceCount > 0 );
  1140. WRITE_REF_TRACE_LOG(
  1141. g_pThreadTraceLog,
  1142. REF_ACTION_ACQUIRE_RESOURCE_EXCLUSIVE,
  1143. pData->ResourceCount,
  1144. pResource,
  1145. pFileName,
  1146. LineNumber
  1147. );
  1148. ULP_DBG_DEREFERENCE_THREAD( pData );
  1149. }
  1150. //
  1151. // Acquire the resource.
  1152. //
  1153. KeEnterCriticalRegion();
  1154. result = ExAcquireResourceExclusive( &pResource->Resource, Wait );
  1155. //
  1156. // either we already own it (recursive acquisition), or nobody owns it.
  1157. //
  1158. ASSERT( UlDbgResourceUnownedExclusive( pResource ) ||
  1159. UlDbgResourceOwnedExclusive( pResource ) );
  1160. //
  1161. // Mark it as owned by the current thread.
  1162. //
  1163. SET_RESOURCE_OWNED_EXCLUSIVE( pResource );
  1164. //
  1165. // Update the statistics.
  1166. //
  1167. InterlockedIncrement( &pResource->ExclusiveCount );
  1168. return result;
  1169. } // UlDbgAcquireResourceExclusive
  1170. /***************************************************************************++
  1171. Routine Description:
  1172. Acquires shared access to an instrumented resource.
  1173. Arguments:
  1174. pResource - Supplies the resource to acquire.
  1175. Wait - Supplies TRUE if the thread should block waiting for the
  1176. resource.
  1177. pFileName - Supplies the filename of the caller.
  1178. LineNumber - Supplies the line number of the caller.
  1179. Return Value:
  1180. BOOLEAN - Completion status.
  1181. --***************************************************************************/
  1182. BOOLEAN
  1183. UlDbgAcquireResourceShared(
  1184. IN PUL_ERESOURCE pResource,
  1185. IN BOOLEAN Wait,
  1186. IN PSTR pFileName,
  1187. IN USHORT LineNumber
  1188. )
  1189. {
  1190. PUL_DEBUG_THREAD_DATA pData;
  1191. BOOLEAN result;
  1192. //
  1193. // Find an existing entry for the current thread.
  1194. //
  1195. pData = ULP_DBG_FIND_THREAD();
  1196. if (pData != NULL)
  1197. {
  1198. //
  1199. // Update the resource count.
  1200. //
  1201. pData->ResourceCount++;
  1202. ASSERT( pData->ResourceCount > 0 );
  1203. WRITE_REF_TRACE_LOG(
  1204. g_pThreadTraceLog,
  1205. REF_ACTION_ACQUIRE_RESOURCE_SHARED,
  1206. pData->ResourceCount,
  1207. pResource,
  1208. pFileName,
  1209. LineNumber
  1210. );
  1211. ULP_DBG_DEREFERENCE_THREAD( pData );
  1212. }
  1213. //
  1214. // Acquire the resource.
  1215. //
  1216. KeEnterCriticalRegion();
  1217. result = ExAcquireResourceShared( &pResource->Resource, Wait );
  1218. //
  1219. // Update the statistics.
  1220. //
  1221. InterlockedIncrement( &pResource->SharedCount );
  1222. return result;
  1223. } // UlDbgAcquireResourceShared
  1224. /***************************************************************************++
  1225. Routine Description:
  1226. Releases an instrumented resource.
  1227. Arguments:
  1228. pResource - Supplies the resource to release.
  1229. pFileName - Supplies the filename of the caller.
  1230. LineNumber - Supplies the line number of the caller.
  1231. --***************************************************************************/
  1232. VOID
  1233. UlDbgReleaseResource(
  1234. IN PUL_ERESOURCE pResource,
  1235. IN PSTR pFileName,
  1236. IN USHORT LineNumber
  1237. )
  1238. {
  1239. PUL_DEBUG_THREAD_DATA pData;
  1240. //
  1241. // Find an existing entry for the current thread.
  1242. //
  1243. pData = ULP_DBG_FIND_THREAD();
  1244. if (pData != NULL)
  1245. {
  1246. //
  1247. // Update the resource count.
  1248. //
  1249. ASSERT( pData->ResourceCount > 0 );
  1250. pData->ResourceCount--;
  1251. WRITE_REF_TRACE_LOG(
  1252. g_pThreadTraceLog,
  1253. REF_ACTION_RELEASE_RESOURCE,
  1254. pData->ResourceCount,
  1255. pResource,
  1256. pFileName,
  1257. LineNumber
  1258. );
  1259. ULP_DBG_DEREFERENCE_THREAD( pData );
  1260. }
  1261. //
  1262. // Mark it as unowned.
  1263. //
  1264. SET_RESOURCE_NOT_OWNED_EXCLUSIVE( pResource );
  1265. //
  1266. // Release the resource.
  1267. //
  1268. ExReleaseResource( &pResource->Resource );
  1269. KeLeaveCriticalRegion();
  1270. //
  1271. // Update the statistics.
  1272. //
  1273. InterlockedIncrement( &pResource->ReleaseCount );
  1274. } // UlDbgReleaseResource
  1275. /***************************************************************************++
  1276. Routine Description:
  1277. This routine converts the specified resource from acquired for exclusive
  1278. access to acquired for shared access.
  1279. Arguments:
  1280. pResource - Supplies the resource to release.
  1281. pFileName - Supplies the filename of the caller.
  1282. LineNumber - Supplies the line number of the caller.
  1283. --***************************************************************************/
  1284. VOID
  1285. UlDbgConvertExclusiveToShared(
  1286. IN PUL_ERESOURCE pResource,
  1287. IN PSTR pFileName,
  1288. IN USHORT LineNumber
  1289. )
  1290. {
  1291. PUL_DEBUG_THREAD_DATA pData;
  1292. ASSERT(UlDbgResourceOwnedExclusive(pResource));
  1293. //
  1294. // Find an existing entry for the current thread.
  1295. //
  1296. pData = ULP_DBG_FIND_THREAD();
  1297. if (pData != NULL)
  1298. {
  1299. //
  1300. // Don't update the resource count.
  1301. //
  1302. WRITE_REF_TRACE_LOG(
  1303. g_pThreadTraceLog,
  1304. REF_ACTION_CONVERT_RESOURCE_EXCLUSIVE_TO_SHARED,
  1305. pData->ResourceCount,
  1306. pResource,
  1307. pFileName,
  1308. LineNumber
  1309. );
  1310. ULP_DBG_DEREFERENCE_THREAD( pData );
  1311. }
  1312. //
  1313. // Acquire the resource.
  1314. //
  1315. ExConvertExclusiveToSharedLite( &pResource->Resource );
  1316. //
  1317. // Update the statistics.
  1318. //
  1319. InterlockedIncrement( &pResource->SharedCount );
  1320. SET_RESOURCE_NOT_OWNED_EXCLUSIVE( pResource );
  1321. } // UlDbgConvertExclusiveToShared
  1322. #ifdef UL_TRY_RESOURCE_EXCLUSIVE
  1323. // ExTryToAcquireResourceExclusiveLite is not currently exported
  1324. // from ntoskrnl
  1325. /***************************************************************************++
  1326. Routine Description:
  1327. The routine attempts to acquire the specified resource for exclusive
  1328. access.
  1329. Arguments:
  1330. pResource - Supplies the resource to release.
  1331. pFileName - Supplies the filename of the caller.
  1332. LineNumber - Supplies the line number of the caller.
  1333. --***************************************************************************/
  1334. BOOLEAN
  1335. UlDbgTryToAcquireResourceExclusive(
  1336. IN PUL_ERESOURCE pResource,
  1337. IN PSTR pFileName,
  1338. IN USHORT LineNumber
  1339. )
  1340. {
  1341. PUL_DEBUG_THREAD_DATA pData;
  1342. BOOLEAN result;
  1343. //
  1344. // Sanity check.
  1345. //
  1346. ASSERT(pResource);
  1347. ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
  1348. //
  1349. // Acquire the resource.
  1350. //
  1351. KeEnterCriticalRegion();
  1352. result = ExTryToAcquireResourceExclusiveLite( &pResource->Resource );
  1353. // Did we acquire the lock exclusively?
  1354. if (! result)
  1355. {
  1356. KeLeaveCriticalRegion();
  1357. return FALSE;
  1358. }
  1359. //
  1360. // Find an existing entry for the current thread.
  1361. //
  1362. pData = ULP_DBG_FIND_THREAD();
  1363. if (pData != NULL)
  1364. {
  1365. //
  1366. // Update the resource count.
  1367. //
  1368. pData->ResourceCount++;
  1369. ASSERT( pData->ResourceCount > 0 );
  1370. WRITE_REF_TRACE_LOG(
  1371. g_pThreadTraceLog,
  1372. REF_ACTION_TRY_ACQUIRE_RESOURCE_EXCLUSIVE,
  1373. pData->ResourceCount,
  1374. pResource,
  1375. pFileName,
  1376. LineNumber
  1377. );
  1378. ULP_DBG_DEREFERENCE_THREAD( pData );
  1379. }
  1380. //
  1381. // either we already own it (recursive acquisition), or nobody owns it.
  1382. //
  1383. ASSERT( UlDbgResourceUnownedExclusive( pResource ) ||
  1384. UlDbgResourceOwnedExclusive( pResource ) );
  1385. //
  1386. // Mark it as owned by the current thread.
  1387. //
  1388. SET_RESOURCE_OWNED_EXCLUSIVE( pResource );
  1389. //
  1390. // Update the statistics.
  1391. //
  1392. InterlockedIncrement( &pResource->ExclusiveCount );
  1393. return result;
  1394. } // UlDbgTryToAcquireResourceExclusive
  1395. #endif // UL_TRY_RESOURCE_EXCLUSIVE
  1396. /***************************************************************************++
  1397. Routine Description:
  1398. Determines if the specified resource is owned exclusively by the
  1399. current thread.
  1400. Arguments:
  1401. pResource - Supplies the resource to test.
  1402. Return Value:
  1403. BOOLEAN - TRUE if the resource is owned exclusively by the current
  1404. thread, FALSE otherwise.
  1405. --***************************************************************************/
  1406. BOOLEAN
  1407. UlDbgResourceOwnedExclusive(
  1408. IN PUL_ERESOURCE pResource
  1409. )
  1410. {
  1411. if (pResource->pExclusiveOwner == PsGetCurrentThread())
  1412. {
  1413. return TRUE;
  1414. }
  1415. // CODEWORK: handle the case of recursive exclusive locks correctly
  1416. return FALSE;
  1417. } // UlDbgResourceOwnedExclusive
  1418. /***************************************************************************++
  1419. Routine Description:
  1420. Determines if the specified resource is not currently owned exclusively
  1421. by any thread.
  1422. Arguments:
  1423. pResource - Supplies the resource to test.
  1424. Return Value:
  1425. BOOLEAN - TRUE if the resource is not currently owned exclusively by
  1426. any thread, FALSE otherwise.
  1427. --***************************************************************************/
  1428. BOOLEAN
  1429. UlDbgResourceUnownedExclusive(
  1430. IN PUL_ERESOURCE pResource
  1431. )
  1432. {
  1433. if (pResource->pExclusiveOwner == NULL)
  1434. {
  1435. return TRUE;
  1436. }
  1437. return FALSE;
  1438. } // UlDbgResourceUnownedExclusive
  1439. VOID
  1440. UlDbgDumpRequestBuffer(
  1441. IN struct _UL_REQUEST_BUFFER *pBuffer,
  1442. IN PSTR pName
  1443. )
  1444. {
  1445. DbgPrint(
  1446. "%s @ %p\n"
  1447. " Signature = %08lx\n"
  1448. " ListEntry @ %p%s\n"
  1449. " pConnection = %p\n"
  1450. " WorkItem @ %p\n"
  1451. " UsedBytes = %lu\n"
  1452. " AllocBytes = %lu\n"
  1453. " ParsedBytes = %lu\n"
  1454. " BufferNumber = %lu\n"
  1455. " JumboBuffer = %lu\n"
  1456. " pBuffer @ %p\n",
  1457. pName,
  1458. pBuffer,
  1459. pBuffer->Signature,
  1460. &pBuffer->ListEntry,
  1461. IsListEmpty( &pBuffer->ListEntry ) ? " EMPTY" : "",
  1462. pBuffer->pConnection,
  1463. &pBuffer->WorkItem,
  1464. pBuffer->UsedBytes,
  1465. pBuffer->AllocBytes,
  1466. pBuffer->ParsedBytes,
  1467. pBuffer->BufferNumber,
  1468. pBuffer->JumboBuffer,
  1469. &pBuffer->pBuffer[0]
  1470. );
  1471. } // UlDbgDumpRequestBuffer
  1472. VOID
  1473. UlDbgDumpHttpConnection(
  1474. IN struct _UL_HTTP_CONNECTION *pConnection,
  1475. IN PSTR pName
  1476. )
  1477. {
  1478. DbgPrint(
  1479. "%s @ %p\n"
  1480. " Signature = %08lx\n"
  1481. " ConnectionId = %08lx%08lx\n"
  1482. " WorkItem @ %p\n"
  1483. " RefCount = %lu\n"
  1484. " NextRecvNumber = %lu\n"
  1485. " NextBufferNumber = %lu\n"
  1486. " NextBufferToParse = %lu\n"
  1487. " pConnection = %p\n"
  1488. " pRequest = %p\n",
  1489. pName,
  1490. pConnection,
  1491. pConnection->Signature,
  1492. pConnection->ConnectionId,
  1493. &pConnection->WorkItem,
  1494. pConnection->RefCount,
  1495. pConnection->NextRecvNumber,
  1496. pConnection->NextBufferNumber,
  1497. pConnection->NextBufferToParse,
  1498. pConnection->pConnection,
  1499. pConnection->pRequest
  1500. );
  1501. DbgPrint(
  1502. "%s @ %p (cont.)\n"
  1503. " Resource @ %p\n"
  1504. " BufferHead @ %p%s\n"
  1505. " pCurrentBuffer = %p\n"
  1506. " NeedMoreData = %lu\n"
  1507. #if REFERENCE_DEBUG
  1508. " pTraceLog = %p\n"
  1509. #endif
  1510. ,
  1511. pName,
  1512. pConnection,
  1513. &pConnection->Resource,
  1514. &pConnection->BufferHead,
  1515. IsListEmpty( &pConnection->BufferHead ) ? " EMPTY" : "",
  1516. pConnection->pCurrentBuffer,
  1517. pConnection->NeedMoreData
  1518. #if REFERENCE_DEBUG
  1519. ,
  1520. pConnection->pTraceLog
  1521. #endif
  1522. );
  1523. } // UlDbgDumpHttpConnection
  1524. PIRP
  1525. UlDbgAllocateIrp(
  1526. IN CCHAR StackSize,
  1527. IN BOOLEAN ChargeQuota,
  1528. IN PSTR pFileName,
  1529. IN USHORT LineNumber
  1530. )
  1531. {
  1532. PIRP pIrp;
  1533. pIrp = IoAllocateIrp( StackSize, ChargeQuota );
  1534. if (pIrp != NULL)
  1535. {
  1536. WRITE_IRP_TRACE_LOG(
  1537. g_pIrpTraceLog,
  1538. IRP_ACTION_ALLOCATE_IRP,
  1539. pIrp,
  1540. pFileName,
  1541. LineNumber
  1542. );
  1543. }
  1544. return pIrp;
  1545. } // UlDbgAllocateIrp
  1546. BOOLEAN g_ReallyFreeIrps = TRUE;
  1547. VOID
  1548. UlDbgFreeIrp(
  1549. IN PIRP pIrp,
  1550. IN PSTR pFileName,
  1551. IN USHORT LineNumber
  1552. )
  1553. {
  1554. WRITE_IRP_TRACE_LOG(
  1555. g_pIrpTraceLog,
  1556. IRP_ACTION_FREE_IRP,
  1557. pIrp,
  1558. pFileName,
  1559. LineNumber
  1560. );
  1561. if (g_ReallyFreeIrps)
  1562. {
  1563. IoFreeIrp( pIrp );
  1564. }
  1565. } // UlDbgFreeIrp
  1566. NTSTATUS
  1567. UlDbgCallDriver(
  1568. IN PDEVICE_OBJECT pDeviceObject,
  1569. IN OUT PIRP pIrp,
  1570. IN PSTR pFileName,
  1571. IN USHORT LineNumber
  1572. )
  1573. {
  1574. PUL_DEBUG_THREAD_DATA pData;
  1575. NTSTATUS Status;
  1576. //
  1577. // Record the fact that we are about to call another
  1578. // driver in the thread data. That way if the driver
  1579. // calls our completion routine in-line our debug
  1580. // code won't get confused about it.
  1581. //
  1582. //
  1583. // Find an existing entry for the current thread.
  1584. //
  1585. pData = ULP_DBG_FIND_THREAD();
  1586. if (pData != NULL)
  1587. {
  1588. //
  1589. // Update the external call count.
  1590. //
  1591. pData->ExternalCallCount++;
  1592. ASSERT( pData->ExternalCallCount > 0 );
  1593. }
  1594. WRITE_IRP_TRACE_LOG(
  1595. g_pIrpTraceLog,
  1596. IRP_ACTION_CALL_DRIVER,
  1597. pIrp,
  1598. pFileName,
  1599. LineNumber
  1600. );
  1601. //
  1602. // Call the driver.
  1603. //
  1604. Status = IoCallDriver( pDeviceObject, pIrp );
  1605. //
  1606. // Update the external call count.
  1607. //
  1608. if (pData != NULL)
  1609. {
  1610. pData->ExternalCallCount--;
  1611. ASSERT( pData->ExternalCallCount >= 0 );
  1612. ULP_DBG_DEREFERENCE_THREAD( pData );
  1613. }
  1614. return Status;
  1615. } // UlDbgCallDriver
  1616. VOID
  1617. UlDbgCompleteRequest(
  1618. IN PIRP pIrp,
  1619. IN CCHAR PriorityBoost,
  1620. IN PSTR pFileName,
  1621. IN USHORT LineNumber
  1622. )
  1623. {
  1624. WRITE_IRP_TRACE_LOG(
  1625. g_pIrpTraceLog,
  1626. IRP_ACTION_COMPLETE_IRP,
  1627. pIrp,
  1628. pFileName,
  1629. LineNumber
  1630. );
  1631. IoCompleteRequest( pIrp, PriorityBoost );
  1632. } // UlDbgCompleteRequest
  1633. PMDL
  1634. UlDbgAllocateMdl(
  1635. IN PVOID VirtualAddress,
  1636. IN ULONG Length,
  1637. IN BOOLEAN SecondaryBuffer,
  1638. IN BOOLEAN ChargeQuota,
  1639. IN OUT PIRP Irp,
  1640. IN PSTR pFileName,
  1641. IN USHORT LineNumber
  1642. )
  1643. {
  1644. PMDL mdl;
  1645. mdl = IoAllocateMdl(
  1646. VirtualAddress,
  1647. Length,
  1648. SecondaryBuffer,
  1649. ChargeQuota,
  1650. Irp
  1651. );
  1652. if (mdl != NULL)
  1653. {
  1654. WRITE_REF_TRACE_LOG(
  1655. g_pMdlTraceLog,
  1656. REF_ACTION_ALLOCATE_MDL,
  1657. PtrToLong(mdl->Next), // bugbug64
  1658. mdl,
  1659. pFileName,
  1660. LineNumber
  1661. );
  1662. #ifdef SPECIAL_MDL_FLAG
  1663. ASSERT( (mdl->MdlFlags & SPECIAL_MDL_FLAG) == 0 );
  1664. #endif
  1665. }
  1666. return mdl;
  1667. } // UlDbgAllocateMdl
  1668. BOOLEAN g_ReallyFreeMdls = TRUE;
  1669. VOID
  1670. UlDbgFreeMdl(
  1671. IN PMDL Mdl,
  1672. IN PSTR pFileName,
  1673. IN USHORT LineNumber
  1674. )
  1675. {
  1676. WRITE_REF_TRACE_LOG(
  1677. g_pMdlTraceLog,
  1678. REF_ACTION_FREE_MDL,
  1679. PtrToLong(Mdl->Next), // bugbug64
  1680. Mdl,
  1681. pFileName,
  1682. LineNumber
  1683. );
  1684. #ifdef SPECIAL_MDL_FLAG
  1685. ASSERT( (Mdl->MdlFlags & SPECIAL_MDL_FLAG) == 0 );
  1686. #endif
  1687. if (g_ReallyFreeMdls)
  1688. {
  1689. IoFreeMdl( Mdl );
  1690. }
  1691. } // UlDbgFreeMdl
  1692. //
  1693. // Private functions.
  1694. //
  1695. /***************************************************************************++
  1696. Routine Description:
  1697. Updates a pool counter.
  1698. Arguments:
  1699. pAddend - Supplies the counter to update.
  1700. Increment - Supplies the value to add to the counter.
  1701. --***************************************************************************/
  1702. VOID
  1703. UlpDbgUpdatePoolCounter(
  1704. IN OUT PLARGE_INTEGER pAddend,
  1705. IN SIZE_T Increment
  1706. )
  1707. {
  1708. ULONG tmp;
  1709. tmp = (ULONG)Increment;
  1710. ASSERT( (SIZE_T)tmp == Increment );
  1711. ExInterlockedAddLargeStatistic(
  1712. pAddend,
  1713. tmp
  1714. );
  1715. } // UlpDbgUpdatePoolCounter
  1716. /***************************************************************************++
  1717. Routine Description:
  1718. Locates the file part of a fully qualified path.
  1719. Arguments:
  1720. pPath - Supplies the path to scan.
  1721. Return Value:
  1722. PSTR - The file part.
  1723. --***************************************************************************/
  1724. PSTR
  1725. UlpDbgFindFilePart(
  1726. IN PSTR pPath
  1727. )
  1728. {
  1729. PSTR pFilePart;
  1730. //
  1731. // Strip off the path from the path.
  1732. //
  1733. pFilePart = strrchr( pPath, '\\' );
  1734. if (pFilePart == NULL)
  1735. {
  1736. pFilePart = pPath;
  1737. }
  1738. else
  1739. {
  1740. pFilePart++;
  1741. }
  1742. return pFilePart;
  1743. } // UlpDbgFindFilePart
  1744. /***************************************************************************++
  1745. Routine Description:
  1746. Locates and optionally creates per-thread data for the current thread.
  1747. Return Value:
  1748. PUL_DEBUG_THREAD_DATA - The thread data if successful, NULL otherwise.
  1749. --***************************************************************************/
  1750. PUL_DEBUG_THREAD_DATA
  1751. UlpDbgFindThread(
  1752. BOOLEAN OkToCreate,
  1753. PSTR pFileName,
  1754. USHORT LineNumber
  1755. )
  1756. {
  1757. PUL_DEBUG_THREAD_DATA pData;
  1758. PUL_THREAD_HASH_BUCKET pBucket;
  1759. PETHREAD pThread;
  1760. KIRQL oldIrql;
  1761. PLIST_ENTRY pListEntry;
  1762. ULONG refCount;
  1763. //
  1764. // Get the current thread, find the correct bucket.
  1765. //
  1766. pThread = PsGetCurrentThread();
  1767. pBucket = &g_DbgThreadHashBuckets[HASH_FROM_THREAD(pThread)];
  1768. //
  1769. // Lock the bucket.
  1770. //
  1771. UlAcquireSpinLock( &pBucket->BucketSpinLock, &oldIrql );
  1772. //
  1773. // Try to find an existing entry for the current thread.
  1774. //
  1775. for (pListEntry = pBucket->BucketListHead.Flink ;
  1776. pListEntry != &pBucket->BucketListHead ;
  1777. pListEntry = pListEntry->Flink)
  1778. {
  1779. pData = CONTAINING_RECORD(
  1780. pListEntry,
  1781. UL_DEBUG_THREAD_DATA,
  1782. ThreadDataListEntry
  1783. );
  1784. if (pData->pThread == pThread)
  1785. {
  1786. //
  1787. // Found one. Update the reference count, then return the
  1788. // existing entry.
  1789. //
  1790. pData->ReferenceCount++;
  1791. refCount = pData->ReferenceCount;
  1792. UlReleaseSpinLock( &pBucket->BucketSpinLock, oldIrql );
  1793. //
  1794. // Trace it.
  1795. //
  1796. WRITE_REF_TRACE_LOG(
  1797. g_pThreadTraceLog,
  1798. REF_ACTION_REFERENCE_THREAD,
  1799. refCount,
  1800. pData,
  1801. pFileName,
  1802. LineNumber
  1803. );
  1804. return pData;
  1805. }
  1806. }
  1807. //
  1808. // If we made it this far, then data has not yet been created for
  1809. // the current thread. Create & initialize it now if we're allowed.
  1810. // Basically it's only ok if we're called from UlDbgEnterDriver.
  1811. //
  1812. if (OkToCreate)
  1813. {
  1814. pData = (PUL_DEBUG_THREAD_DATA) UL_ALLOCATE_POOL(
  1815. NonPagedPool,
  1816. sizeof(*pData),
  1817. UL_DEBUG_THREAD_POOL_TAG
  1818. );
  1819. if (pData != NULL)
  1820. {
  1821. RtlZeroMemory( pData, sizeof(*pData) );
  1822. pData->pThread = pThread;
  1823. pData->ReferenceCount = 1;
  1824. pData->ResourceCount = 0;
  1825. InsertHeadList(
  1826. &pBucket->BucketListHead,
  1827. &pData->ThreadDataListEntry
  1828. );
  1829. InterlockedIncrement( &g_DbgThreadCreated );
  1830. }
  1831. }
  1832. else
  1833. {
  1834. pData = NULL;
  1835. }
  1836. UlReleaseSpinLock( &pBucket->BucketSpinLock, oldIrql );
  1837. return pData;
  1838. } // UlpDbgFindThread
  1839. /***************************************************************************++
  1840. Routine Description:
  1841. Dereferences per-thread data.
  1842. Arguments:
  1843. pData - Supplies the thread data to dereference.
  1844. --***************************************************************************/
  1845. VOID
  1846. UlpDbgDereferenceThread(
  1847. IN PUL_DEBUG_THREAD_DATA pData
  1848. REFERENCE_DEBUG_FORMAL_PARAMS
  1849. )
  1850. {
  1851. PUL_THREAD_HASH_BUCKET pBucket;
  1852. KIRQL oldIrql;
  1853. ULONG refCount;
  1854. //
  1855. // Find the correct bucket.
  1856. //
  1857. pBucket = &g_DbgThreadHashBuckets[HASH_FROM_THREAD(pData->pThread)];
  1858. //
  1859. // Update the reference count.
  1860. //
  1861. UlAcquireSpinLock( &pBucket->BucketSpinLock, &oldIrql );
  1862. ASSERT( pData->ReferenceCount > 0 );
  1863. pData->ReferenceCount--;
  1864. refCount = pData->ReferenceCount;
  1865. if (pData->ReferenceCount == 0)
  1866. {
  1867. //
  1868. // It dropped to zero, so remove the thread from the bucket
  1869. // and free the resources.
  1870. //
  1871. RemoveEntryList( &pData->ThreadDataListEntry );
  1872. UlReleaseSpinLock( &pBucket->BucketSpinLock, oldIrql );
  1873. UL_FREE_POOL( pData, UL_DEBUG_THREAD_POOL_TAG );
  1874. InterlockedIncrement( &g_DbgThreadDestroyed );
  1875. }
  1876. else
  1877. {
  1878. UlReleaseSpinLock( &pBucket->BucketSpinLock, oldIrql );
  1879. }
  1880. //
  1881. // Trace it.
  1882. //
  1883. WRITE_REF_TRACE_LOG(
  1884. g_pThreadTraceLog,
  1885. REF_ACTION_DEREFERENCE_THREAD,
  1886. refCount,
  1887. pData,
  1888. pFileName,
  1889. LineNumber
  1890. );
  1891. } // UlpDbgDereferenceThread
  1892. #endif // DBG