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.

1089 lines
27 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. stktrace.c
  5. Abstract:
  6. This module implements routines to snapshot a set of stack back traces
  7. in a data base. Useful for heap allocators to track allocation requests
  8. cheaply.
  9. Author:
  10. Steve Wood (stevewo) 29-Jan-1992
  11. Revision History:
  12. 17-May-1999 (silviuc) : added RtlWalkFrameChain that replaces the
  13. unsafe RtlCaptureStackBackTrace.
  14. 29-Jul-2000 (silviuc) : added RtlCaptureStackContext.
  15. 6-Nov-2000 (silviuc): IA64 runtime stack traces.
  16. 18-Feb-2001 (silviuc) : moved all x86 specific code into i386 directory.
  17. 03-May-2002 (silviuc) : switched to a resource instead of a lock. Traces that
  18. are in the database can be found by locking the resource in shared mode.
  19. Only a real addition to the database will require exclusive acquisition.
  20. --*/
  21. #include <ntos.h>
  22. #include <ntrtl.h>
  23. #include "ntrtlp.h"
  24. #include <nturtl.h>
  25. #include <zwapi.h>
  26. #include <stktrace.h>
  27. #include <heap.h>
  28. #include <heappriv.h>
  29. //
  30. // Number of buckets used for the simple chaining hash table.
  31. //
  32. #define NUMBER_OF_BUCKETS 1567
  33. //
  34. // Macros to hide the different synchronization routines for
  35. // user mode and kernel mode runtimes. For kernel runtime
  36. // the OKAY_TO_LOCK macro points to a real function that makes
  37. // sure current thread is not executing a DPC routine.
  38. //
  39. #ifdef NTOS_KERNEL_RUNTIME
  40. typedef struct _KSPIN_LOCK_EX {
  41. KSPIN_LOCK Lock;
  42. KIRQL OldIrql;
  43. PKTHREAD Owner;
  44. } KSPIN_LOCK_EX, *PKSPIN_LOCK_EX;
  45. NTSTATUS
  46. KeInitializeSpinLockEx (
  47. PKSPIN_LOCK_EX Lock
  48. )
  49. {
  50. KeInitializeSpinLock (&(Lock->Lock));
  51. Lock->OldIrql = 0;
  52. Lock->Owner = NULL;
  53. return STATUS_SUCCESS;
  54. }
  55. VOID
  56. KeAcquireSpinLockEx (
  57. PKSPIN_LOCK_EX Lock
  58. )
  59. {
  60. KeAcquireSpinLock (&(Lock->Lock), &(Lock->OldIrql));
  61. Lock->Owner = KeGetCurrentThread();
  62. }
  63. VOID
  64. KeReleaseSpinLockEx (
  65. PKSPIN_LOCK_EX Lock
  66. )
  67. {
  68. Lock->Owner = NULL;
  69. KeReleaseSpinLock (&(Lock->Lock), (Lock->OldIrql));
  70. }
  71. #define INITIALIZE_DATABASE_LOCK(R) KeInitializeSpinLockEx((PKSPIN_LOCK_EX)R)
  72. #define ACQUIRE_DATABASE_LOCK(R) KeAcquireSpinLockEx((PKSPIN_LOCK_EX)R)
  73. #define RELEASE_DATABASE_LOCK(R) KeReleaseSpinLockEx((PKSPIN_LOCK_EX)R)
  74. #define OKAY_TO_LOCK_DATABASE(R) ExOkayToLockRoutine(&(((PKSPIN_LOCK_EX)R)->Lock))
  75. BOOLEAN
  76. ExOkayToLockRoutine (
  77. IN PVOID Lock
  78. );
  79. #else //#ifdef NTOS_KERNEL_RUNTIME
  80. #define INITIALIZE_DATABASE_LOCK(R) RtlInitializeCriticalSection(R)
  81. #define ACQUIRE_DATABASE_LOCK(R) RtlEnterCriticalSection(R)
  82. #define RELEASE_DATABASE_LOCK(R) RtlLeaveCriticalSection(R)
  83. #define OKAY_TO_LOCK_DATABASE(R) (RtlDllShutdownInProgress() == FALSE)
  84. #endif // #ifdef NTOS_KERNEL_RUNTIME
  85. //
  86. // Globals from elsewhere refered here.
  87. //
  88. extern BOOLEAN RtlpFuzzyStackTracesEnabled;
  89. //
  90. // Forward declarations of private functions.
  91. //
  92. USHORT
  93. RtlpLogStackBackTraceEx(
  94. ULONG FramesToSkip
  95. );
  96. LOGICAL
  97. RtlpCaptureStackTraceForLogging (
  98. PRTL_STACK_TRACE_ENTRY Trace,
  99. PULONG Hash,
  100. ULONG FramesToSkip,
  101. LOGICAL UserModeStackFromKernelMode
  102. );
  103. USHORT
  104. RtlpLogCapturedStackTrace(
  105. PRTL_STACK_TRACE_ENTRY Trace,
  106. ULONG Hash
  107. );
  108. PRTL_STACK_TRACE_ENTRY
  109. RtlpExtendStackTraceDataBase(
  110. IN PRTL_STACK_TRACE_ENTRY InitialValue,
  111. IN SIZE_T Size
  112. );
  113. //
  114. // Global per process (user mode) or system wide (kernel mode)
  115. // stack trace database.
  116. //
  117. PSTACK_TRACE_DATABASE RtlpStackTraceDataBase;
  118. //
  119. // Resource used to control access to stack trace database. We opted for
  120. // this solution so that we kept change in the database structure to an
  121. // absolute minimal. This way the tools that depend on this structure
  122. // (at least umhd and oh) will not need a new version and will not
  123. // introduce backcompatibility issues.
  124. //
  125. #ifdef NTOS_KERNEL_RUNTIME
  126. KSPIN_LOCK_EX RtlpStackTraceDataBaseLock;
  127. #else
  128. RTL_CRITICAL_SECTION RtlpStackTraceDataBaseLock;
  129. #endif
  130. /////////////////////////////////////////////////////////////////////
  131. //////////////////////////////////////// Runtime stack trace database
  132. /////////////////////////////////////////////////////////////////////
  133. //
  134. // The following section implements a trace database used to store
  135. // stack traces captured with RtlCaptureStackBackTrace(). The database
  136. // is implemented as a hash table and does not allow deletions. It is
  137. // sensitive to "garbage" in the sense that spurios garbage (partially
  138. // correct stacks) will hash in different buckets and will tend to fill
  139. // the whole table. This is a problem only on x86 if "fuzzy" stack traces
  140. // are used. The typical function used to log the trace is
  141. // RtlLogStackBackTrace. One of the worst limitations of this package
  142. // is that traces are refered using a ushort index which means we cannot
  143. // ever store more than 65535 traces (remember we never delete traces).
  144. //
  145. PSTACK_TRACE_DATABASE
  146. RtlpAcquireStackTraceDataBase(
  147. )
  148. {
  149. PSTACK_TRACE_DATABASE DataBase;
  150. DataBase = RtlpStackTraceDataBase;
  151. //
  152. // Sanity checks.
  153. //
  154. if (DataBase == NULL) {
  155. return NULL;
  156. }
  157. if (! OKAY_TO_LOCK_DATABASE (DataBase->Lock)) {
  158. return NULL;
  159. }
  160. ACQUIRE_DATABASE_LOCK (DataBase->Lock);
  161. if (DataBase->DumpInProgress) {
  162. RELEASE_DATABASE_LOCK (DataBase->Lock);
  163. return NULL;
  164. }
  165. else {
  166. return DataBase;
  167. }
  168. }
  169. VOID
  170. RtlpReleaseStackTraceDataBase(
  171. )
  172. {
  173. PSTACK_TRACE_DATABASE DataBase;
  174. DataBase = RtlpStackTraceDataBase;
  175. //
  176. // Sanity checks.
  177. //
  178. if (DataBase == NULL) {
  179. return;
  180. }
  181. RELEASE_DATABASE_LOCK (DataBase->Lock);
  182. }
  183. NTSTATUS
  184. RtlInitializeStackTraceDataBase(
  185. IN PVOID CommitBase,
  186. IN SIZE_T CommitSize,
  187. IN SIZE_T ReserveSize
  188. )
  189. {
  190. NTSTATUS Status;
  191. PSTACK_TRACE_DATABASE DataBase;
  192. //
  193. // On x86 where runtime stack tracing algorithms are unreliable
  194. // if we have a big enough trace database then we can enable fuzzy
  195. // stack traces that do not hash very well and have the potential
  196. // to fill out the trace database.
  197. //
  198. #if defined(_X86_) && !defined(NTOS_KERNEL_RUNTIME)
  199. if (ReserveSize >= 16 * RTL_MEG) {
  200. RtlpFuzzyStackTracesEnabled = TRUE;
  201. }
  202. #endif
  203. DataBase = (PSTACK_TRACE_DATABASE)CommitBase;
  204. if (CommitSize == 0) {
  205. //
  206. // Initially commit enough pages to accomodate the increased
  207. // number of hash chains (for improved performance we switched from ~100
  208. // to ~1000 in the hope that the hash chains will decrease ten-fold in
  209. // length).
  210. //
  211. CommitSize = ROUND_TO_PAGES (NUMBER_OF_BUCKETS * sizeof (DataBase->Buckets[ 0 ]));
  212. Status = ZwAllocateVirtualMemory (NtCurrentProcess(),
  213. (PVOID *)&CommitBase,
  214. 0,
  215. &CommitSize,
  216. MEM_COMMIT,
  217. PAGE_READWRITE);
  218. if (! NT_SUCCESS(Status)) {
  219. KdPrint (("RTL: Unable to commit space to extend stack "
  220. "trace data base - Status = %lx\n",
  221. Status));
  222. return Status;
  223. }
  224. DataBase->PreCommitted = FALSE;
  225. }
  226. else if (CommitSize == ReserveSize) {
  227. RtlZeroMemory (DataBase, sizeof( *DataBase ));
  228. DataBase->PreCommitted = TRUE;
  229. }
  230. else {
  231. return STATUS_INVALID_PARAMETER;
  232. }
  233. DataBase->CommitBase = CommitBase;
  234. DataBase->NumberOfBuckets = NUMBER_OF_BUCKETS;
  235. DataBase->NextFreeLowerMemory = (PCHAR)(&DataBase->Buckets[ DataBase->NumberOfBuckets ]);
  236. DataBase->NextFreeUpperMemory = (PCHAR)CommitBase + ReserveSize;
  237. if (! DataBase->PreCommitted) {
  238. DataBase->CurrentLowerCommitLimit = (PCHAR)CommitBase + CommitSize;
  239. DataBase->CurrentUpperCommitLimit = (PCHAR)CommitBase + ReserveSize;
  240. }
  241. else {
  242. RtlZeroMemory (&DataBase->Buckets[ 0 ],
  243. DataBase->NumberOfBuckets * sizeof (DataBase->Buckets[ 0 ]));
  244. }
  245. DataBase->EntryIndexArray = (PRTL_STACK_TRACE_ENTRY *)DataBase->NextFreeUpperMemory;
  246. //
  247. // Initialize the database lock.
  248. //
  249. DataBase->Lock = &RtlpStackTraceDataBaseLock;
  250. Status = INITIALIZE_DATABASE_LOCK (DataBase->Lock);
  251. if (! NT_SUCCESS(Status)) {
  252. KdPrint(("RTL: Unable to initialize stack trace database lock (status %X)\n", Status));
  253. return Status;
  254. }
  255. RtlpStackTraceDataBase = DataBase;
  256. return STATUS_SUCCESS;
  257. }
  258. PRTL_STACK_TRACE_ENTRY
  259. RtlpExtendStackTraceDataBase(
  260. IN PRTL_STACK_TRACE_ENTRY InitialValue,
  261. IN SIZE_T Size
  262. )
  263. /*++
  264. Routine Description:
  265. This routine extends the stack trace database in order to accomodate
  266. the new stack trace that has to be saved.
  267. Arguments:
  268. InitialValue - stack trace to be saved.
  269. Size - size of the stack trace in bytes. Note that this is not the
  270. depth of the trace but rather `Depth * sizeof(PVOID)'.
  271. Return Value:
  272. The address of the just saved stack trace or null in case we have hit
  273. the maximum size of the database or we get commit errors.
  274. Environment:
  275. User mode.
  276. Note. In order to make all this code work in kernel mode we have to
  277. rewrite this function that relies on VirtualAlloc.
  278. --*/
  279. {
  280. NTSTATUS Status;
  281. PRTL_STACK_TRACE_ENTRY p, *pp;
  282. SIZE_T CommitSize;
  283. PSTACK_TRACE_DATABASE DataBase;
  284. DataBase = RtlpStackTraceDataBase;
  285. //
  286. // We will try to find space for one stack trace entry in the
  287. // upper part of the database.
  288. //
  289. pp = (PRTL_STACK_TRACE_ENTRY *)DataBase->NextFreeUpperMemory;
  290. if ((! DataBase->PreCommitted) &&
  291. ((PCHAR)(pp - 1) < (PCHAR)DataBase->CurrentUpperCommitLimit)) {
  292. //
  293. // No more committed space in the upper part of the database.
  294. // We need to extend it downwards.
  295. //
  296. DataBase->CurrentUpperCommitLimit =
  297. (PVOID)((PCHAR)DataBase->CurrentUpperCommitLimit - PAGE_SIZE);
  298. if (DataBase->CurrentUpperCommitLimit < DataBase->CurrentLowerCommitLimit) {
  299. //
  300. // No more space at all. We have got over the lower part of the db.
  301. // We failed therefore increase back the UpperCommitLimit pointer.
  302. //
  303. DataBase->CurrentUpperCommitLimit =
  304. (PVOID)((PCHAR)DataBase->CurrentUpperCommitLimit + PAGE_SIZE);
  305. return( NULL );
  306. }
  307. CommitSize = PAGE_SIZE;
  308. Status = ZwAllocateVirtualMemory(
  309. NtCurrentProcess(),
  310. (PVOID *)&DataBase->CurrentUpperCommitLimit,
  311. 0,
  312. &CommitSize,
  313. MEM_COMMIT,
  314. PAGE_READWRITE
  315. );
  316. if (!NT_SUCCESS( Status )) {
  317. //
  318. // We tried to increase the upper part of the db by one page.
  319. // We failed therefore increase back the UpperCommitLimit pointer
  320. //
  321. DataBase->CurrentUpperCommitLimit =
  322. (PVOID)((PCHAR)DataBase->CurrentUpperCommitLimit + PAGE_SIZE);
  323. return NULL;
  324. }
  325. }
  326. //
  327. // We managed to make sure we have usable space in the upper part
  328. // therefore we take out one stack trace entry address.
  329. //
  330. DataBase->NextFreeUpperMemory -= sizeof( *pp );
  331. //
  332. // Now we will try to find space in the lower part of the database for
  333. // for the eactual stack trace.
  334. //
  335. p = (PRTL_STACK_TRACE_ENTRY)DataBase->NextFreeLowerMemory;
  336. if ((! DataBase->PreCommitted) &&
  337. (((PCHAR)p + Size) > (PCHAR)DataBase->CurrentLowerCommitLimit)) {
  338. //
  339. // We need to extend the lower part.
  340. //
  341. if (DataBase->CurrentLowerCommitLimit >= DataBase->CurrentUpperCommitLimit) {
  342. //
  343. // We have hit the maximum size of the database.
  344. //
  345. return( NULL );
  346. }
  347. //
  348. // Extend the lower part of the database by one page.
  349. //
  350. CommitSize = Size;
  351. Status = ZwAllocateVirtualMemory(
  352. NtCurrentProcess(),
  353. (PVOID *)&DataBase->CurrentLowerCommitLimit,
  354. 0,
  355. &CommitSize,
  356. MEM_COMMIT,
  357. PAGE_READWRITE
  358. );
  359. if (! NT_SUCCESS( Status )) {
  360. return NULL;
  361. }
  362. DataBase->CurrentLowerCommitLimit =
  363. (PCHAR)DataBase->CurrentLowerCommitLimit + CommitSize;
  364. }
  365. //
  366. // Take out the space for the stack trace.
  367. //
  368. DataBase->NextFreeLowerMemory += Size;
  369. //
  370. // Deal with a precommitted database case. If the lower and upper
  371. // pointers have crossed each other then rollback and return failure.
  372. //
  373. if (DataBase->PreCommitted &&
  374. DataBase->NextFreeLowerMemory >= DataBase->NextFreeUpperMemory) {
  375. DataBase->NextFreeUpperMemory += sizeof( *pp );
  376. DataBase->NextFreeLowerMemory -= Size;
  377. return( NULL );
  378. }
  379. //
  380. // Save the stack trace in the database
  381. //
  382. RtlMoveMemory( p, InitialValue, Size );
  383. p->HashChain = NULL;
  384. p->TraceCount = 0;
  385. p->Index = (USHORT)(++DataBase->NumberOfEntriesAdded);
  386. //
  387. // Save the address of the new stack trace entry in the
  388. // upper part of the databse.
  389. //
  390. *--pp = p;
  391. //
  392. // Return address of the saved stack trace entry.
  393. //
  394. return( p );
  395. }
  396. #pragma optimize("y", off) // disable FPO
  397. USHORT
  398. RtlLogStackBackTrace(
  399. VOID
  400. )
  401. /*++
  402. Routine Description:
  403. This routine will capture the current stacktrace (skipping the
  404. present function) and will save it in the global (per process)
  405. stack trace database. It should be noted that we do not save
  406. duplicate traces.
  407. Arguments:
  408. None.
  409. Return Value:
  410. Index of the stack trace saved. The index can be used by tools
  411. to access quickly the trace data. This is the reason at the end of
  412. the database we save downwards a list of pointers to trace entries.
  413. This index can be used to find this pointer in constant time.
  414. A zero index will be returned for error conditions (e.g. stack
  415. trace database not initialized).
  416. Environment:
  417. User mode.
  418. --*/
  419. {
  420. return RtlpLogStackBackTraceEx (1);
  421. }
  422. #pragma optimize("y", off) // disable FPO
  423. USHORT
  424. RtlpLogStackBackTraceEx(
  425. ULONG FramesToSkip
  426. )
  427. /*++
  428. Routine Description:
  429. This routine will capture the current stacktrace (skipping the
  430. present function) and will save it in the global (per process)
  431. stack trace database. It should be noted that we do not save
  432. duplicate traces.
  433. Arguments:
  434. FramesToSkip - no of frames that are not interesting and
  435. should be skipped.
  436. Return Value:
  437. Index of the stack trace saved. The index can be used by tools
  438. to access quickly the trace data. This is the reason at the end of
  439. the database we save downwards a list of pointers to trace entries.
  440. This index can be used to find this pointer in constant time.
  441. A zero index will be returned for error conditions (e.g. stack
  442. trace database not initialized).
  443. Environment:
  444. User mode.
  445. --*/
  446. {
  447. RTL_STACK_TRACE_ENTRY Trace;
  448. USHORT TraceIndex;
  449. NTSTATUS Status;
  450. ULONG Hash;
  451. PSTACK_TRACE_DATABASE DataBase;
  452. //
  453. // Check the context in which we are running.
  454. //
  455. DataBase = RtlpStackTraceDataBase;
  456. if (DataBase == NULL) {
  457. return 0;
  458. }
  459. if (! OKAY_TO_LOCK_DATABASE (DataBase->Lock)) {
  460. return 0;
  461. }
  462. //
  463. // Capture stack trace.
  464. //
  465. if (RtlpCaptureStackTraceForLogging (&Trace, &Hash, FramesToSkip + 1, FALSE) == FALSE) {
  466. return 0;
  467. }
  468. //
  469. // Add the trace if it is not already there.
  470. // Return trace index.
  471. //
  472. TraceIndex = RtlpLogCapturedStackTrace (&Trace, Hash);
  473. return TraceIndex;
  474. }
  475. #if defined(NTOS_KERNEL_RUNTIME)
  476. #pragma optimize("y", off) // disable FPO
  477. USHORT
  478. RtlLogUmodeStackBackTrace(
  479. VOID
  480. )
  481. /*++
  482. Routine Description:
  483. This routine will capture the user mode stacktrace and will save
  484. it in the global (per system) stack trace database.
  485. It should be noted that we do not save duplicate traces.
  486. Arguments:
  487. None.
  488. Return Value:
  489. Index of the stack trace saved. The index can be used by tools
  490. to access quickly the trace data. This is the reason at the end of
  491. the database we save downwards a list of pointers to trace entries.
  492. This index can be used to find this pointer in constant time.
  493. A zero index will be returned for error conditions (e.g. stack
  494. trace database not initialized).
  495. Environment:
  496. User mode.
  497. --*/
  498. {
  499. RTL_STACK_TRACE_ENTRY Trace;
  500. ULONG Hash;
  501. //
  502. // No database => nothing to do.
  503. //
  504. if (RtlpStackTraceDataBase == NULL) {
  505. return 0;
  506. }
  507. //
  508. // Capture user mode stack trace.
  509. //
  510. if (RtlpCaptureStackTraceForLogging (&Trace, &Hash, 1, TRUE) == FALSE) {
  511. return 0;
  512. }
  513. //
  514. // Add the trace if it is not already there.
  515. // Return trace index.
  516. //
  517. return RtlpLogCapturedStackTrace (&Trace, Hash);
  518. }
  519. #endif // #if defined(NTOS_KERNEL_RUNTIME)
  520. #pragma optimize("y", off) // disable FPO
  521. LOGICAL
  522. RtlpCaptureStackTraceForLogging (
  523. PRTL_STACK_TRACE_ENTRY Trace,
  524. PULONG Hash,
  525. ULONG FramesToSkip,
  526. LOGICAL UserModeStackFromKernelMode
  527. )
  528. {
  529. if (UserModeStackFromKernelMode == FALSE) {
  530. //
  531. // Capture stack trace. The try/except was useful
  532. // in the old days when the function did not validate
  533. // the stack frame chain. We keep it just to be defensive.
  534. //
  535. try {
  536. Trace->Depth = RtlCaptureStackBackTrace (FramesToSkip + 1,
  537. MAX_STACK_DEPTH,
  538. Trace->BackTrace,
  539. Hash);
  540. }
  541. except(EXCEPTION_EXECUTE_HANDLER) {
  542. Trace->Depth = 0;
  543. }
  544. if (Trace->Depth == 0) {
  545. return FALSE;
  546. }
  547. else {
  548. return TRUE;
  549. }
  550. }
  551. else {
  552. #ifdef NTOS_KERNEL_RUNTIME
  553. ULONG Index;
  554. //
  555. // Avoid weird situations.
  556. //
  557. if (KeAreAllApcsDisabled () == TRUE) {
  558. return FALSE;
  559. }
  560. //
  561. // Capture user mode stack trace and hash value.
  562. //
  563. Trace->Depth = (USHORT) RtlWalkFrameChain(Trace->BackTrace,
  564. MAX_STACK_DEPTH,
  565. 1);
  566. if (Trace->Depth == 0) {
  567. return FALSE;
  568. }
  569. else {
  570. *Hash = 0;
  571. for (Index = 0; Index < Trace->Depth; Index += 1) {
  572. *Hash += PtrToUlong (Trace->BackTrace[Index]);
  573. }
  574. return TRUE;
  575. }
  576. #else //#ifdef NTOS_KERNEL_RUNTIME
  577. return FALSE;
  578. #endif // #ifdef NTOS_KERNEL_RUNTIME
  579. }
  580. }
  581. USHORT
  582. RtlpLogCapturedStackTrace(
  583. PRTL_STACK_TRACE_ENTRY Trace,
  584. ULONG Hash
  585. )
  586. {
  587. PSTACK_TRACE_DATABASE DataBase;
  588. PRTL_STACK_TRACE_ENTRY p, *pp;
  589. ULONG RequestedSize, DepthSize;
  590. USHORT ReturnValue;
  591. DataBase = RtlpStackTraceDataBase;
  592. //
  593. // Update statistics counters. Since they are used only for reference and do not
  594. // control decisions we increment them without protection even if this means we may
  595. // have numbers slightly out of sync.
  596. //
  597. DataBase->NumberOfEntriesLookedUp += 1;
  598. //
  599. // Lock the global per-process stack trace database.
  600. //
  601. if (RtlpAcquireStackTraceDataBase() == NULL) {
  602. //
  603. // Fail the log operation if we cannot acquire the lock.
  604. // This can happen only if there is a dump in progress or we are in
  605. // an invalid context (process shutdown (Umode) or DPC routine (Kmode).
  606. //
  607. return 0;
  608. }
  609. try {
  610. //
  611. // We will try to find out if the trace has been saved in the past.
  612. // We find the right hash chain and then traverse it.
  613. //
  614. DepthSize = Trace->Depth * sizeof (Trace->BackTrace[0]);
  615. pp = &DataBase->Buckets[ Hash % DataBase->NumberOfBuckets ];
  616. while (p = *pp) {
  617. //
  618. // ISSUE: SilviuC: we should use hash values in comparing traces.
  619. // Comparing first hash values and depth should save us a lot of
  620. // compares pointer by pointer.
  621. //
  622. if (p->Depth == Trace->Depth) {
  623. if (RtlCompareMemory( &p->BackTrace[ 0 ], &Trace->BackTrace[ 0 ], DepthSize) == DepthSize) {
  624. break;
  625. }
  626. }
  627. pp = &p->HashChain;
  628. }
  629. if (p == NULL) {
  630. //
  631. // If we get here we did not find a similar trace in the database. We need
  632. // to add it.
  633. //
  634. // We got the `*pp' value (address of last chain element) while the
  635. // database lock was acquired shared so we need to take into consideration
  636. // the case where another thread managed to acquire database exclusively
  637. // and add a new trace at the end of the chain. Therefore if `*pp' is no longer
  638. // null we continue to traverse the chain until we get to the end.
  639. //
  640. p = NULL;
  641. if (*pp != NULL) {
  642. //
  643. // Somebody added some traces at the end of the chain while we
  644. // were trying to convert the lock from shared to exclusive.
  645. //
  646. while (p = *pp) {
  647. if (p->Depth == Trace->Depth) {
  648. if (RtlCompareMemory( &p->BackTrace[ 0 ], &Trace->BackTrace[ 0 ], DepthSize) == DepthSize) {
  649. break;
  650. }
  651. }
  652. pp = &p->HashChain;
  653. }
  654. }
  655. if (p == NULL) {
  656. //
  657. // Nobody added the trace and now `*pp' really points to the end
  658. // of the chain either because we traversed the rest of the chain
  659. // or it was at the end anyway.
  660. //
  661. RequestedSize = FIELD_OFFSET (RTL_STACK_TRACE_ENTRY, BackTrace) + DepthSize;
  662. p = RtlpExtendStackTraceDataBase (Trace, RequestedSize);
  663. if (p != NULL) {
  664. //
  665. // We added the trace no chain it as the last element.
  666. //
  667. *pp = p;
  668. }
  669. }
  670. else {
  671. //
  672. // Some other thread managed to add the same trace to the database
  673. // while we were trying to acquire the lock exclusive. `p' has the
  674. // address to the stack trace entry.
  675. //
  676. }
  677. }
  678. }
  679. except(EXCEPTION_EXECUTE_HANDLER) {
  680. //
  681. // We should never get here if the algorithm is correct.
  682. //
  683. p = NULL;
  684. }
  685. //
  686. // Release locks and return. At this stage we may return zero (failure)
  687. // if we did not manage to extend the database with a new trace (e.g. due to
  688. // out of memory conditions).
  689. //
  690. if (p != NULL) {
  691. p->TraceCount += 1;
  692. ReturnValue = p->Index;
  693. }
  694. else {
  695. ReturnValue = 0;
  696. }
  697. RtlpReleaseStackTraceDataBase();
  698. return ReturnValue;
  699. }
  700. PVOID
  701. RtlpGetStackTraceAddress (
  702. USHORT Index
  703. )
  704. {
  705. if (RtlpStackTraceDataBase == NULL) {
  706. return NULL;
  707. }
  708. if (! (Index > 0 && Index <= RtlpStackTraceDataBase->NumberOfEntriesAdded)) {
  709. return NULL;
  710. }
  711. return (PVOID)(RtlpStackTraceDataBase->EntryIndexArray[-Index]);
  712. }
  713. BOOLEAN
  714. RtlpCaptureStackLimits (
  715. ULONG_PTR HintAddress,
  716. PULONG_PTR StartStack,
  717. PULONG_PTR EndStack)
  718. /*++
  719. Routine Description:
  720. This routine figures out what are the stack limits for the current thread.
  721. This is used in other routines that need to grovel the stack for various
  722. information (e.g. potential return addresses).
  723. The function is especially tricky in K-mode where the information kept in
  724. the thread structure about stack limits is not always valid because the
  725. thread might execute a DPC routine and in this case we use a different stack
  726. with different limits.
  727. Arguments:
  728. HintAddress - Address of a local variable or parameter of the caller of the
  729. function that should be the start of the stack.
  730. StartStack - start address of the stack (lower value).
  731. EndStack - end address of the stack (upper value).
  732. Return value:
  733. False if some weird condition is discovered, like an End lower than a Start.
  734. --*/
  735. {
  736. #ifdef NTOS_KERNEL_RUNTIME
  737. //
  738. // Avoid weird conditions. Doing this in an ISR is never a good idea.
  739. //
  740. if (KeGetCurrentIrql() > DISPATCH_LEVEL) {
  741. return FALSE;
  742. }
  743. *StartStack = (ULONG_PTR)(KeGetCurrentThread()->StackLimit);
  744. *EndStack = (ULONG_PTR)(KeGetCurrentThread()->StackBase);
  745. if (*StartStack <= HintAddress && HintAddress <= *EndStack) {
  746. *StartStack = HintAddress;
  747. }
  748. else {
  749. #if defined(_WIN64)
  750. //
  751. // On Win64 we do not know yet where DPCs are executed.
  752. //
  753. return FALSE;
  754. #else
  755. *EndStack = (ULONG_PTR)(KeGetPcr()->Prcb->DpcStack);
  756. #endif
  757. *StartStack = *EndStack - KERNEL_STACK_SIZE;
  758. //
  759. // Check if this is within the DPC stack for the current
  760. // processor.
  761. //
  762. if (*EndStack && *StartStack <= HintAddress && HintAddress <= *EndStack) {
  763. *StartStack = HintAddress;
  764. }
  765. else {
  766. //
  767. // This is not current thread's stack and is not the DPC stack
  768. // of the current processor. Basically we have no idea on what
  769. // stack we are running. We need to investigate this. On free
  770. // builds we try to make the best out of it by using only one
  771. // page for stack limits.
  772. //
  773. // SilviuC: I disabled the code below because it seems under certain
  774. // conditions drivers do indeed switch execution to a different stack.
  775. // This function will need to be improved to deal with this too.
  776. //
  777. #if 0
  778. DbgPrint ("RtlpCaptureStackLimits: mysterious stack (prcb @ %p) \n",
  779. KeGetPcr()->Prcb);
  780. DbgBreakPoint ();
  781. #endif
  782. *StartStack = HintAddress;
  783. *EndStack = (*StartStack + PAGE_SIZE) & ~((ULONG_PTR)PAGE_SIZE - 1);
  784. }
  785. }
  786. #else
  787. *StartStack = HintAddress;
  788. *EndStack = (ULONG_PTR)(NtCurrentTeb()->NtTib.StackBase);
  789. #endif
  790. return TRUE;
  791. }